Teoria R
Teoria R
Teoria R
Introducción al tratamiento
de datos con R y RStudio
Módulo 1
Lección 0
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 0
Logística de R
R es un entorno de programación para el análisis estadístico y gráfico de datos muy popular, cada
día más utilizado en empresas y universidades. Su uso tiene muchas ventajas. Para empezar,
es software libre. La elección de software libre es, en general, acertada por varios motivos: por
un lado, transmite valores socialmente positivos, como por ejemplo la libertad individual, el
conocimiento compartido, la solidaridad y la cooperación; por otro, nos aproxima al método
científico, porque permite el examen y mejora del código desarrollado por otros usuarios y la
reproducibilidad de los resultados obtenidos; finalmente, pero no menos importante desde un
punto de vista práctico, podemos adquirir de manera legal y gratuita copias del programa, sin
necesidad de licencias personales o académicas.
Aparte de su faceta de software libre, R tiene algunas ventajas específicas: por ejemplo, su
sintaxis básica es sencilla e intuitiva, con la que es muy fácil familiarizarse, lo que se traduce en
un aprendizaje rápido y cómodo; además, tiene una enorme comunidad de usuarios, estructura-
da alrededor de la Comprehensive R Archive Network, CRAN, que desarrolla cada día nuevos
paquetes1 que extienden sus funcionalidades y cubren casi todas las necesidades computacio-
nales y estadísticas de un científico o ingeniero. En fin, si necesitáis aún más razones para usar
R, podéis consultar la página «Why use R?» de la organización inside-R, en el url
http://www.inside-r.org/why-use-r
(2) Si sois usuarios de Windows, entrad en el enlace «base», descargad R y seguid las instruc-
ciones de instalación del documento «Installation and other instructions» que encontraréis
en esa misma página. Si usáis Windows Vista, conviene que consultéis además el enlace
«How do I install R when using Windows Vista?».
Si sois usuarios de Mac OS X, descargad el fichero .pkg correspondiente y, una vez
descargado, abridlo y seguid las instrucciones del Asistente de Instalación.
1
En enero de 2016, el repositorio de paquetes de la CRAN, http://cran.r-project.org/web/packages/,
superó los 7500 paquetes.
2
Las sesiones de R que aparecen en estas notas se han llevado a cabo en la versión 3.2.3.
Si trabajáis con Ubuntu o Debian, para instalar la última versión de R basta que ejecutéis en
una terminal, estando conectados a Internet, la siguiente instrucción:
sudo aptitude install r-base
Cuando instaláis R para Windows o Mac OS X, con él también se os instala una interfaz gráfica
que se abrirá al abrir la aplicación y en la que podréis trabajar. La instalación para Linux no
lleva una interfaz por defecto, así que sus usuarios tienen que trabajar con R en la terminal
(ejecutando R para iniciar una sesión) o instalar aparte una interfaz. Independientemente de
todas estas posibilidades, en este curso usaremos RStudio como interfaz gráfica de usuario de
R para todos los sistemas operativos.
Propiamente hablando, RStudio es mucho más que una interfaz de R: se trata de todo un
entorno integrado para utilizar y programar con R, que dispone de un conjunto de herramientas
que facilitan el trabajo con este lenguaje. Para instalarlo, se ha de descargar de
http://www.rstudio.com/products/rstudio/download/
la versión correspondiente al sistema operativo en el que se trabaja. Una vez descargado, en el
caso de Windows o Mac OS X ya se puede abrir directamente. En el caso de Linux, hay que
ejecutar en una terminal la siguiente instrucción para completar su instalación:
sudo dpkg -i rstudio-<version>-i386.deb
donde version refiere a la versión concreta que se haya descargado. Conviene recordar que
RStudio no es R, ni tan solo lo contiene: hay que instalar ambos programas. De hecho, las
instalaciones de R y RStudio son independientes una de la otra, de manera que cuando se pone
al día uno de estos programas, no se modifica el otro.
Cuando se abre RStudio, aparece una ventana similar a la que muestra la Figura 1: su
apariencia exacta dependerá del sistema operativo. De momento, nos concentraremos en la
ventana de la izquierda, llamada la consola de R. Observaréis que en el momento de abrir la
aplicación, dicha ventana contiene una serie de información (versión, créditos, etc.) y al final
una línea en blanco encabezada por el símbolo >. Este símbolo es la marca de inicio e indica
que R espera que escribáis alguna instrucción y la ejecutéis.
0-2
Durante la mayor parte de este curso, usaremos RStudio de manera interactiva:
(4) R abrirá una nueva línea en blanco encabezada por una marca de inicio, donde esperará
una nueva instrucción.
Haced una prueba: escribid 1+1 junto a la marca de inicio y pulsad Entrar; R escribirá en la línea
siguiente el resultado de la suma, 2, y a continuación una nueva línea en blanco encabezada
por la marca de inicio.
> 1+1
[1] 2
>
Ya hablaremos del [1] que precede al 2 en la Lección 3. Hasta entonces, no os preocupéis por
él.
Para facilitarnos el trabajo, la consola dispone de un mecanismo para acceder a las instruc-
ciones ya ejecutadas y modificarlas si queremos. Si situamos el cursor a la derecha de la marca
de inicio de la línea inferior y pulsamos la tecla de la flecha vertical ascendente ", iremos obte-
niendo de manera consecutiva, en esa línea, las instrucciones escritas hasta el momento en la
misma sesión; si nos pasamos, podemos usar la tecla # para retroceder dentro de esta lista; una
vez alcanzada la instrucción deseada, podemos volver a ejecutarla o, con las teclas de flechas
horizontales, ir al lugar de la instrucción que queramos y reescribir un trozo antes de ejecutarla.
Otra posibilidad es usar la pestaña History de la ventana superior derecha de RStudio, que
contiene la lista de todas las instrucciones que se han ejecutado en la sesión actual. Si seleccio-
namos una instrucción de esta lista y pulsamos el botón «To console» del menú superior de la
pestaña, la instrucción se copiará en la consola y la podremos modificar o ejecutar directamente.
También podemos copiar instrucciones de otros ficheros y pegarlas a la derecha de la marca
de inicio de la manera habitual en el sistema operativo de nuestro ordenador. Pero hay que ir
con cuidado: las instrucciones copiadas de ficheros en formato que no sea texto simple pueden
contener caracteres invisibles a simple vista que generen errores al intentar ejecutar la instruc-
ción copiada. En particular, esto afecta a las instrucciones que podáis copiar de ficheros en
formato PDF como este. Para evitaros este problema, para cada lección encontraréis, en el re-
positorio del curso, un fichero en formato texto simple llamado Leccion_i.R (donde la i indica
el número de la lección), que contendrá la mayoría de las instrucciones que aparecen en ella.
Estas instrucciones las podréis copiar y pegar en la consola, o podréis ejecutarlas directamente
abriendo el guión en la ventana de ficheros (véase la Sección 0.3).
Volvamos a la ventana de RStudio de la Figura 1. Observaréis que está dividida a su vez
en tres ventanas. La de la izquierda es la consola, donde trabajamos en modo interactivo. La
ventana inferior derecha tiene algunas pestañas, entre las que destacamos:
0-3
Files, que muestra el contenido de la carpeta de trabajo actual (véase la Sección 0.2).
Al hacer clic sobre un fichero en esta lista, se abrirá en la ventana de ficheros (véase la
Sección 0.3).
Plots, que muestra los gráficos que hayamos producido durante la sesión. Se puede na-
vegar entre ellos con las flechas de la barra superior de la pestaña.
Packages, que muestra todos los paquetes instalados y, marcados, los que están cargados
en la sesión actual (véase la Sección 0.5).
Environment, con la lista de los objetos actualmente definidos (véase la Lección 1).
History, de la que ya hemos hablado, que contiene la lista de todas las instrucciones que
hayamos ejecutado durante la sesión.
Aparte de estas tres ventanas, RStudio dispone de una cuarta ventana para ficheros, que se
abre en el sector superior izquierdo, sobre la consola (véase la Sección 0.3).
Para cerrar RStudio, basta elegir «Quit RStudio» del menú «RStudio» o pulsar la combina-
ción de teclas usual para cerrar un programa en vuestro sistema operativo.
Podéis usar el menú «Session ! Set Working Directory ! Choose Directory. . . » para
escoger una carpeta.
Podéis abrir la pestaña Files de la ventana inferior derecha y navegar por el árbol de
directorios que aparece en su barra superior hasta llegar a la carpeta deseada.
Tanto de una manera como de la otra, la carpeta que especifiquéis será la carpeta de trabajo
durante lo que queda de sesión o hasta que la volváis a cambiar.
En cualquier momento podéis guardar la sesión en la que estéis trabajando usando el menú
«Session ! Save Workspace as. . . ». Además, si no habéis modificado esta opción en las Pre-
ferencias, cuando cerréis RStudio se os pedirá si queréis guardar la sesión; si contestáis que sí,
RStudio guardará en la carpeta de trabajo dos ficheros, .RData y .RHistory, que se cargarán
automáticamente al volver a abrir RStudio y estaréis exactamente donde lo habíais dejado.
0-4
Nuestro consejo es que digáis que no: normalmente, no os interesará arrastrar todo lo que ha-
yáis hecho en sesiones anteriores. Y si queréis guardar algunas definiciones e instrucciones de
una sesión, lo más práctico es guardarlas en un guión (véase la Sección 0.3).
Los gráficos que generéis con RStudio aparecerán en la ventana inferior derecha, en la pestaña
Plots que se activa automáticamente cuando se crea alguno. La apariencia del gráfico dependerá
de las dimensiones de esta ventana, por lo que es conveniente que sea cuadrada si queréis que el
gráfico no aparezca achatado o estirado. Si modificáis la forma de la ventana, las dimensiones
del gráfico que aparezca en ella se modificarán de manera automática.
Para guardar un gráfico, hay que ir al menú «Export» de esta ventana y seleccionar cómo
queréis guardarlo: como una imagen en uno los formatos estándares de imágenes (.png, .jpeg,
.tiff, etc.) o en formato PDF. Entonces, se abrirá una ventana donde podéis darle nombre,
modificar sus dimensiones y especificar el directorio donde queráis que se guarde, entre otras
opciones.
0-5
de salida. Una vez completada esta información, se abrirá el fichero en la ventana superior
izquierda.
Por poner un ejemplo, supongamos que habéis elegido realizar un informe («Document») con
formato de salida html (los formatos posibles son: pdf, html o Word); entonces, para generar
un informe básico basta sustituir las palabras clave que ha generado RStudio en esta ventana.
Probadlo: cambiad el título y el texto; a continuación, guardad el fichero con el nombre que
queráis y extensión .Rmd, y pulsad el botón «Knit html» situado en la barra superior de
la ventana; se generará un fichero html en la carpeta de trabajo, con el texto del fichero R
Markdown y el mismo título cambiando la extensión .Rmd por .html, y se abrirá en una
ventana aparte.
Aprender los primeros pasos de R Markdown es sencillo. Para ello, id al botón «?» situado
en la barra superior de la ventana de ficheros y escoged «Markdown Quick Reference»; os
aparecerá, en la ventana de ayuda, un manual de referencia rápida de R Markdown que se
puede leer en 15 minutos y que para la mayoría de ejercicios de este curso es más que suficiente.
También os puede ser útil la chuleta de R Markdown rm-cheatsheet.pdf que encontraréis en
el repositorio del curso y en su url original:
http://shiny.rstudio.com/images/rm-cheatsheet.pdf.zip
En cualquier caso, a medida que avance el curso iremos explicando técnicas para mejorar los
ficheros resultantes.
0-6
help.search("palabra clave") o, equivalentemente, ??palabra clave
(las comillas en el help.search son obligatorias). De esta manera, conseguiremos en la ventana
de Ayuda una lista de las funciones que R entiende que están relacionadas con la palabra clave
entrada. Entonces, pulsando en la función que nos interese de esta lista, aparecerá la información
específica sobre ella. Como podéis imaginar, conviene que la palabra clave esté en inglés.
R dispone también de una página web de ayuda en línea, que se abre en la pestaña de Ayuda
entrando la instrucción help.start().4
Además de la ayuda que incorpora el mismo R, siempre podéis acudir a foros y listas de
discusión para encontrar ayuda sobre cualquier duda que podáis tener. Algunos recursos que
nosotros encontramos especialmente útiles son los siguientes:
Es muy probable que alguien ya haya tenido la misma dificultad y se la hayan resuelto en
alguno de estos foros.
Existe también una comunidad muy activa de usuarios hispanos de R, en cuyo portal web
encontraréis muchos recursos útiles para mejorar vuestro conocimiento de este lenguaje:
http://r-es.org/Comunidad
0-7
En caso de necesitar un paquete que no tengamos instalado, hay que instalarlo antes de
poderlo cargar. La mayoría de los paquetes se pueden instalar desde el repositorio del CRAN;
esto se puede hacer de dos maneras:
Así, supongamos que queremos construir cuadrados mágicos, pero aún no hemos cargado el
paquete magic.
> magic (10)
Error : could not find function " magic "
> install . packages ( " magic " , dep = TRUE ) # Instalamos el paquete magic ;
tambi é n lo pod é is hacer a trav é s de la ventana de paquetes
...
> library ( magic ) # Cargamos el paquete ; tambi é n lo pod é is hacer a
trav é s de la ventana de paquetes
...
> magic (10)
[ ,1] [ ,2] [ ,3] [ ,4] [ ,5] [ ,6] [ ,7] [ ,8] [ ,9] [ ,10]
[1 ,] 34 35 6 7 98 99 70 71 42 43
[2 ,] 36 33 8 5 100 97 72 69 44 41
[3 ,] 11 10 83 82 75 74 47 46 39 38
[4 ,] 12 9 84 81 73 76 48 45 40 37
[5 ,] 87 86 79 78 51 50 23 22 15 14
[6 ,] 85 88 77 80 52 49 21 24 13 16
[7 ,] 63 62 55 54 27 26 19 18 91 90
[8 ,] 61 64 53 56 25 28 17 20 89 92
[9 ,] 59 58 31 30 3 2 95 94 67 66
[10 ,] 57 60 29 32 1 4 93 96 65 68
Cuando cerramos RStudio, los paquetes instalados en la sesión siguen instalados, pero carga-
dos se pierden; por lo tanto, si queremos volver a usarlos en otra sesión, tendremos que volver
a cargarlos.
Hay paquetes que no se encuentran en el CRAN y que, por lo tanto, no se pueden instalar
de la forma que hemos visto. Cuando sea necesario, ya explicaremos la manera de instalarlos y
cargarlos en cada caso.
0-8
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 1
Lección 1
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 1
La calculadora
Cuando se trabaja en modo interactivo en la consola de R, hay que escribir las instrucciones a
la derecha de la marca de inicio > de la línea inferior. Para evaluar una instrucción al terminar
de escribirla, se tiene que pulsar la tecla Entrar ( -); así, por ejemplo, si junto a la marca de
inicio escribimos 2+3 y pulsamos Entrar, R escribirá en la línea siguiente el resultado, 5, y a
continuación una nueva línea en blanco encabezada por la marca de inicio, donde podremos
continuar entrando instrucciones.
> 2+3 # Y ahora aqu í pulso Entrar
[1] 5
>
Bueno, hemos hecho trampa. Como ya habíamos comentado en la Sección 0.3, se pueden
escribir comentarios: R ignora todo lo que se escribe en la línea después de un signo #. También
podéis observar que R ha dado el resultado en una línea que empieza con [1]; ya discutiremos
en la Lección 3 qué significa este [1].
Si la expresión que entramos no está completa, R no la evaluará y en la línea siguiente esperará
a que la acabemos; lo indicará con la marca de continuación, por defecto un signo +.1 Además,
si cometemos algún error de sintaxis, R nos avisará con un mensaje de error.2
> 2 * (3+5 # Pulso Entrar , pero no he acabado
+ ) # ahora s í
[1] 16
> 2 * 3+5)
Error : unexpected ’) ’ in " 2 * 3+5) "
Se puede agrupar más de una instrucción en una sola línea separándolas con signos de punto
y coma. Al pulsar la tecla Entrar, R las ejecutará todas, una tras otra y en el orden en el que
las hayamos escrito.
> 2+3; 2+4; 2+5
[1] 5
[1] 6
1
En estas notas, y excepto en el ejemplo que damos en esta página, no mostraremos este signo + para no
confundirlo con una suma.
2
Cuando instaláis R, éste reconoce la zona donde vivís a partir de información que extrae del ordenador, y
especifica el idioma de los mensajes de acuerdo con esta zona. Seguramente, por lo tanto, los mensajes de error
y advertencias os aparecerán en castellano; un castellano algo chapucero, la verdad. Si los queréis en inglés,
entrad la instrucción
system("defaults write org.R-project.R force.LANG en_US.UTF-8")
y, a continuación, cerrad RStudio y volvedlo a abrir. Si los tenéis en inglés, o en cualquier otro idioma, y los
queréis en castellano, tenéis que usar
system("defaults write org.R-project.R force.LANG es_Es.UTF-8").
[1] 7
Las operaciones usuales se indican en R con los signos que damos en la Tabla 1.1. Por lo que
se refiere a los dos últimos operadores en esta tabla, recordad que si a y b son dos números
reales, con b > 0, la división entera de a por b da como cociente entero el mayor número entero
q tal que q · b 6 a, y como resto la diferencia a q · b. Por ejemplo, la división entera de 29.5
entre 6.3 es 29.5 = 4 · 6.3 + 4.3, con cociente entero 4 y resto 4.3.4
3
Por consistencia, en el texto también seguiremos el convenio angloamericano de usar un punto en lugar de
una coma como separador decimal.
4
Cuando b < 0, R da como cociente entero el menor número entero q tal que q · b > a, y como resto la diferencia
a q · b, que en este caso es negativa.
1-2
[1] 0.03125
> 534 %/ %7 # ¿ Cu á ntas semanas completas caben en 534 d í as ?
[1] 76
> 534 % %7 # ¿ Y cu á ntos d í as sobran ?
[1] 2
> 534 - 76 * 7
[1] 2
Cuando un número es muy grande o muy pequeño, R emplea la llamada notación científica
para dar una aproximación.
> 2^40
[1] 1.099512 e +12
> 2^( - 20)
[1] 9.536743 e - 07
1-3
y es igual al número de maneras posibles de escoger un subconjunto de m elementos de un
conjunto de n objetos diferentes. Recordad también que el valor absoluto |x| de un número x
se obtiene tomando x sin signo: | 8| = |8| = 8.
p n
Función x ex ln(x) log10 (x) loga (x) n! m
R entiende que los argumentos de las funciones sin, cos y tan están en radianes. Si queremos
aplicar una de estas funciones a un número de grados, podemos traducir los grados a radianes
multiplicándolos por ⇡/180. De manera similar, los resultados de asin, acos y atan también
están en radianes, y se pueden convertir a grados multiplicándolos por 180/⇡.
> cos (60) # Coseno de 60 radianes
[1] - 0.952413
> cos (60 * pi / 180) # Coseno de 60 grados
[1] 0.5
> acos (0.5) # Arcocoseno de 0.5 en radianes
1-4
[1] 1.047198
> acos (0.5) * 180 / pi # Arcocoseno de 0.5 en grados
[1] 60
> acos (2)
[1] NaN
Este último NaN (acrónimo de Not a Number ) significa que el resultado no existe; en efecto,
arccos(2) no existe como número real, ya que cos(x) siempre pertenece al intervalo [ 1, 1].
Ya hemos visto que R dispone del signo pi para representar el número real ⇡. En cambio, no
tiene ningún signo para indicar la constante de Euler e, y hay que emplear exp(1).
> 2 * exp (1) # 2 · e
[1] 5.436564
> exp ( pi ) - pi ^ exp (1) # e ^ pi - pi ^ e
[1] 0.6815349
5
Si necesitáis trabajar de manera exacta con más cifras significativas, os recomendamos usar las funciones del
paquete Rmpfr. Podéis consultar su manual de referencia en https://cran.r-project.org/web/packages/
Rmpfr/index.html.
6
También podéis especificar este número de cifras para toda una sesión, entrándolo en lugar de los puntos
suspensivos en options(digits=...). Esto no cambia la precisión de los cálculos, sólo cómo se muestran los
resultados.
1-5
[1] 1.2677 e +30
El número máximo de cifras que podemos pedir con print es 22; si pedimos más, R nos dará
un mensaje de error.
> print ( sqrt (2) , 22)
[1] 1.414213562373095145475
> print ( sqrt (2) , 23)
Error in print . default ( sqrt (2) , 23) : invalid ’ digits ’ argument
Por otro lado, hay que tener en cuenta que, como ya hemos comentado, R trabaja con una
precisión de unas 16 cifras decimales y por lo tanto los dígitos más allá de esta precisión pueden
ser incorrectos. Por ejemplo, si le pedimos las 22 primeras cifras de ⇡, obtenemos el resultado
siguiente:
> print ( pi ,22)
[1] 3.141592653589793115998
7
No es un capricho, es la regla de redondeo en caso de empate recomendada por el estándar IEEE 754 para
aritmética en coma flotante.
1-6
[1] 1
> round ( sqrt (2) , 0)
[1] 1
En la página 1-4 ya vimos una función de dos argumentos que toma uno por defecto: log.
Su sintaxis completa es log(x, base=...), y si no especificamos la base, toma su valor por
defecto, e, y calcula el logaritmo neperiano.
La función round(x) redondea x al valor entero más cercano (y en caso de empate, al que
termina en cifra par). R también dispone de otras funciones que permiten redondear a números
enteros en otros sentidos específicos:
floor(x) redondea x a un número entero por defecto, dando el mayor número entero
menor o igual que x, que denotamos por bxc.
ceiling(x) redondea x a un número entero por exceso, dando el menor número entero
mayor o igual que x, que denotamos por dxe.
1-7
[1] - 3
> trunc ( - 3.7) # La parte entera de - 3.7
[1] - 3
> round ( - 3.7) # El entero m á s cercano a - 3.7
[1] - 4
Al entrar esta instrucción, R creará el objeto x y le asignará el valor que hemos especificado.
En general, se puede crear una variable y asignarle un valor, o asignar un nuevo valor a una
variable definida anteriormente, mediante la construcción
nombre_de_la_variable=valor.
También se puede conectar el nombre de la variable con el valor por medio de una flecha -> o
<-, compuesta de un guión y un signo de desigualdad, de manera que el sentido de la flecha vaya
del valor a la variable; por ejemplo, las tres primeras instrucciones siguientes son equivalentes,
y asignan el valor 2 a la variable x, mientras que las dos últimas son incorrectas:
> x =2
> x<-2
> 2 - >x
> 2= x
Error in 2 = x : invalid ( do _ set ) left - hand side to assignment
> 2<-x
Error in 2 < - x : invalid ( do _ set ) left - hand side to assignment
1-8
> x =5
> x ^2
[1] 25
> x = x - 2 # Redefinimos x como su valor actual menos 2
> x
[1] 3
> x ^2
[1] 9
> x = sqrt ( x ) # Redefinimos x como la ra í z cuadrada de su valor actual
> x
[1] 1.732051
Conviene que os acostumbréis a escribir la fórmula que define la función entre llaves {...}.
A veces es necesario y a veces no, pero no vale la pena discutir cuándo.
El nombre de la variable se indica dentro de los paréntesis que siguen al function. En
el ejemplo anterior, la variable era x, y por eso hemos escrito =function(x). Si hubiéramos
querido definir la función con variable t, habríamos usado =function(t) (y, naturalmente,
habríamos escrito la fórmula que define la función con la variable t):
> f = function ( t ) { t ^2 - 2^ t }
Se pueden definir funciones de dos o más variables con function, declarándolas todas. Por
2
ejemplo, para definir la función f (x, y) = e(2x y) , tenemos que entrar
> f = function (x , y ) { exp ((2 * x - y ) ^2) }
Las funciones no tienen por qué tener como argumentos o resultados sólo números reales:
pueden involucrar vectores, matrices, tablas de datos, etc. Y se pueden definir por medio de
secuencias de instrucciones, no sólo mediante fórmulas numéricas directas; en este caso, hay
1-9
que separar las diferentes instrucciones con signos de punto y coma o escribir cada instrucción
en una nueva línea. Ya iremos viendo ejemplos a medida que avance el curso.
En cada momento se pueden saber los objetos (por ejemplo, variables y funciones) que se han
definido en la sesión hasta ese momento entrando la instrucción ls() o consultando la pestaña
Environment. Para borrar la definición de un objeto, hay que aplicarle la función rm. Si se
quiere hacer limpieza y borrar de golpe las definiciones de todos los objetos que se han definido
hasta el momento, se puede emplear la instrucción rm(list=ls()) o usar el botón «Clear» de
la barra superior de la pestaña Environment.
> rm ( list = ls () ) # Borramos todas las definiciones
> f = function ( t ) { t ^2 - 2^ t }
> a =1
> a
[1] 1
> ls ()
[1] " a " " f "
> rm ( a )
> ls ()
[1] " f "
> a
Error : object ’a ’ not found
Fijaos en que cuando entramos en R un número complejo escrito en forma binomial a + bi,
no escribimos un * entre la i y su coeficiente; de hecho, no hay que escribirlo:
> 2+5 * i
Error : object ’i ’ not found
Por otro lado, si el coeficiente de i es 1 o 1, hay que escribir el «1»: por ejemplo, 3 i se tiene
que escribir 3-1i. Si no lo hacemos, R da un mensaje de error.
> (3+ i ) * (2 - i )
Error : object ’i ’ not found
> (3+1 i ) * (2 - 1 i )
[1] 7 - 1 i
1-10
Los complejos que tienen como parte imaginaria un número entero o un racional escrito en
forma decimal se pueden entrar directamente en forma binomial, como lo hemos hecho hasta
ahora. Para definir números complejos más «complejos» se puede usar la función
complex(real=..., imaginary=...).
Veamos un ejemplo:
> 1+2 / 3 i # Esto en realidad es 1 m á s 2 partido por 3 i
[1] 1 - 0.666667 i
> 1+(2 / 3) i
Error : unexpected symbol in " 1+(2 / 3) i "
> complex ( real =1 , imaginary =2 / 3)
[1] 1+0.666667 i
> z =1+ sqrt (2) i
Error : unexpected symbol in " z =1+ sqrt (2) i "
> z = complex ( real =1 , imaginary = sqrt (2) )
> z
[1] 1+1.414214 i
Como sabéis, los números complejos se inventaron para poder trabajar con raíces cuadradas
de números negativos. Ahora bien, por defecto, cuando calculamos la raíz cuadrada de un
número negativo R no devuelve un número complejo, sino que se limita a avisarnos de que no
existe.
> sqrt ( - 3)
[1] NaN
Warning message :
In sqrt ( - 3) : NaNs produced
La mayoría de las funciones que hemos dado para los números reales admiten extensiones
para números complejos, y con R se calculan con la misma función. Ahora no entraremos a
explicar cómo se definen estas extensiones, sólo lo comentamos por si sabéis qué hacen y os
interesa calcularlas.
> sqrt (2+3 i )
[1] 1.674149+0.895977 i
> exp (2+3 i )
[1] - 7.31511+1.042744 i
> sin (2+3 i )
[1] 9.154499 - 4.168907 i
> acos ( as . complex (2) ) # El arcocoseno de 2 es un n ú mero complejo
[1] 0+1.316958 i
1-11
La raíz cuadrada merece un comentario. Naturalmente, sqrt(2+3i) calcula un número com-
plejo z tal que z 2 = 2 + 3i. Como ocurre con los números reales, todo número complejo diferente
de 0 tiene dos raíces cuadradas, y una se obtiene multiplicando la otra por 1. R da como raíz
cuadrada de un número real la positiva, y como raíz cuadrada de un complejo la que tiene parte
real positiva, y si su parte real es 0, la que tiene parte imaginaria positiva.
z = (a, b) = a + bi
b ····························································•
⌘····
⌘ ·····
p ⌘ ····
|z| = a2 + b2⌘⌘ ···
···
⌘ ···· |z| sin(✓z )
⌘ ····
⌘
..⌘ ····
⌘] ...
...
... ✓z
····
⌘ ..... ····
⌘ . ·
|z| cos(✓z ) a
Un número complejo z = a + bi se puede representar como el punto (a, b) del plano cartesiano
R . Esto permite asociarle dos magnitudes geométricas (véase la Figura 1.1):
2
El módulo de z, que denotaremos por |z|, es la distancia euclídea de (0, 0) a (a, b):
p
|z| = a2 + b2 .
El argumento de z (para z 6= 0), que denotaremos por ✓z , es el ángulo que forman el semieje
positivo de abscisas y el vector que va de (0, 0) a (a, b). Este ángulo está determinado por
las ecuaciones
a b
cos(✓z ) = p , sin(✓z ) = p .
a2 + b 2 a2 + b 2
1-12
> Mod (4 - 7 i )
[1] 8.062258
> Arg (4 - 7 i )
[1] - 1.051650
> Conj (4 - 7 i )
[1] 4+7 i
Funciones numéricas:
p n
Función x ex ln(x) log10 (x) loga (x) n! m
pi es el número ⇡.
1-13
floor(x) redondea x a un número entero por defecto.
rm(list=ls()) borra las definiciones de todos los objetos que hayamos definido.
complex se usa para definir números complejos que no se puedan entrar directamente en
forma binomial. Algunos parámetros importantes:
1.7. Ejercicio
Si hubiéramos empezado a contar segundos a partir de las 12 campanadas que marcaron el
inicio de 2015, ¿qué día de qué año llegaríamos a los 250 millones de segundos? ¡Cuidado con
los años bisiestos!
1-14
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 1
Lección 2
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 2
Un aperitivo: Introducción a la regresión
lineal
En muchos libros de texto y artículos científicos encontraréis gráficos donde una línea recta
o algún otro tipo de curva se ajusta a una serie de observaciones representadas por medio de
puntos en el plano. La situación en general es la siguiente. Supongamos que tenemos una serie
de puntos del plano cartesiano R2 ,
que representan pares de observaciones de dos variables numéricas: por ejemplo, x = año e y =
población, o x = longitud de una rama e y = número de hojas en la rama. Queremos describir
cómo depende la variable dependiente y de la variable independiente x a partir de estas observa-
ciones. Para ello, buscaremos una función y = f (x) cuya gráfica se aproxime lo máximo posible
a los puntos (xi , yi )i=1,...,n . Esta función nos dará un modelo matemático del comportamiento
de las observaciones realizadas que nos permitirá entender mejor los mecanismos que relacionan
las variables estudiadas o hacer predicciones sobre futuras observaciones.
Una primera opción, y la más sencilla, es estudiar si los puntos (xi , yi )i=1,...,n satisfacen una
relación lineal. En este caso, se busca la recta de ecuación y = b1 x + b0 , con b0 , b1 2 R, que
aproxime mejor los puntos dados, en el sentido de que la suma de los cuadrados de las diferencias
entre los valores yi y sus aproximaciones b1 xi + b0 ,
n
X
(yi (b1 xi + b0 ))2 ,
i=1
sea mínima. A esta recta y = b1 x + b0 se la llama recta de regresión por mínimos cuadra-
dos; para abreviar, aquí la llamaremos simplemente recta de regresión, porque es la única que
estudiaremos por ahora.
El objetivo de esta lección es ilustrar el uso de R mediante el cálculo de esta recta de regresión.
Para ello, introduciremos algunas funciones de R que ya explicaremos con más detalle en otras
lecciones. Utilizaremos también transformaciones logarítmicas para tratar casos en los que los
puntos dados se aproximen mejor mediante una función potencial o exponencial. Dejaremos
para otro curso el estudio detallado del ajuste de funciones a familias de puntos con R.
R. Aunque en este ejemplo concreto no sería necesario, lo haremos así para que empecéis a
acostumbraros. La ventaja de tener los datos organizados en forma de data frame es que con
ellos luego se pueden hacer muchas más cosas. Estudiaremos en detalle los data frames en la
Lección 5.
Para crear este data frame, en primer lugar guardaremos cada fila de la Tabla 2.1 como un
vector, es decir, como una lista ordenada de números, y le pondremos un nombre adecuado. Para
definir un vector, podemos aplicar la función c a la secuencia ordenada de números, separados
por comas.
> edad = c (1 ,2 ,3 ,5 ,7 ,9 ,11 ,13)
> altura = c (76.11 ,86.45 ,95.27 ,109.18 ,122.03 ,133.73 ,143.73 ,156.41)
> edad
[1] 1 2 3 5 7 9 11 13
> altura
[1] 76.11 86.45 95.27 109.18 122.03 133.73 143.73 156.41
Ahora vamos a construir un data frame de dos columnas, una para la edad y otra para la
altura, y lo llamaremos datos1. Estas columnas serán las variables de nuestra tabla de datos.
Para organizar diversos vectores de la misma longitud en un data frame, podemos aplicar la
función data.frame a los vectores.
> datos1 = data . frame ( edad , altura )
> datos1
edad altura
1 1 76.11
2 2 86.45
3 3 95.27
4 5 109.18
5 7 122.03
6 9 133.73
7 11 143.73
8 13 156.41
Observad que las filas del data frame resultante corresponden a los pares (edad, altura) de la
Tabla 2.1.
Al analizar unos datos, siempre es conveniente empezar con una representación gráfica que
nos permita hacernos una idea de sus características. En este caso, lo primero que haremos
será dibujar los pares (edad, altura) usando la función plot. Esta función tiene muchos pará-
metros que permiten mejorar el resultado, pero ya los veremos al estudiarla en detalle en la
Lección 6. Por ahora nos conformamos con un gráfico básico de estos puntos que nos muestre
su distribución.
Dada una familia de puntos (xn , yn )n=1,...,k , si llamamos x al vector (xn )n=1,...,k de sus abscisas e
y al vector (yn )n=1,...,k de sus ordenadas, podemos obtener el gráfico de los puntos (xn , yn )n=1,...,k
mediante la instrucción
2-2
plot(x,y).
Si los vectores x e y son, en este orden, la primera y la segunda columna de un data frame
de dos variables, es suficiente aplicar la función plot al data frame. Así, por ejemplo, para
dibujar el gráfico de la Figura 2.1 de los puntos (edadn , alturan )n=1,...,8 , basta entrar la siguiente
instrucción:
> plot ( datos1 )
100
80
2 4 6 8 10 12
edad
Vamos a calcular ahora su recta de regresión. Dada una familia de puntos (xn , yn )n=1,...,k , si
llamamos x al vector (xn )n=1,...,k de sus abscisas e y al vector (yn )n=1,...,k de sus ordenadas, su
recta de regresión se calcula con R por medio de la instrucción
lm(y~x).
Fijaos en la sintaxis: dentro del argumento de lm, primero va el vector y, seguido del vector x
conectado a y por una tilde ~. Para R, esta tilde significa «en función de»: es decir, lm(y~x)
significa «la recta de regresión de y en función de x». Para obtener este signo, los usuarios de
Windows y Linux tienen que pulsar Ctrl+Alt+4 seguido de un espacio en blanco y los de Mac
OS X con teclado español pueden pulsar Alt+Ñ seguido de un espacio en blanco.
Si los vectores y y x son, en este orden, la primera y la segunda columna de un data frame de
dos variables, es suficiente aplicar la función lm al data frame. Por desgracia, en nuestro data
frame no aparecen en este orden. En general, si x e y son dos columnas de un data frame, para
calcular la recta de regresión de y en función de x podemos usar la instrucción
lm(y~x, data=data frame).
Así pues, para calcular la recta de regresión de los puntos (edadn , alturan )n=1,...,8 , entramos la
siguiente instrucción:
2-3
> lm ( altura ~ edad , data = datos1 )
Call :
lm ( formula = altura ~ edad , data = datos1 )
Coefficients :
( Intercept ) edad
73.968 6.493
El resultado que hemos obtenido significa que la recta de regresión tiene término independiente
73.968 (el punto donde la recta interseca al eje de las y) y el coeficiente de x es 6.493 (el
coeficiente de la variable edad). Es decir, es la recta
y = 6.493x + 73.968.
Ahora la podemos superponer al gráfico anterior, empleando la función abline. Esta función
permite añadir una recta al gráfico activo en la pestaña Plots. Por lo tanto, si no hemos cerrado
el gráfico anterior, la instrucción
> abline ( lm ( altura ~ edad , data = datos1 ) )
le añade la recta de regresión, produciendo la Figura 2.2. Se ve a simple vista que, efectivamente,
esta recta aproxima muy bien los datos.
140
120
altura
100
80
2 4 6 8 10 12
edad
Es importante tener presente que el análisis que hemos realizado de los pares de valores
(edadn , alturan )n=1,...,8 ha sido puramente descriptivo: hemos mostrado que estos datos son
consistentes con una función lineal, pero no hemos demostrado que la altura media sea función
aproximadamente lineal de la edad. Esto último requeriría una demostración matemática o
un argumento biológico, no una simple comprobación numérica para una muestra pequeña de
valores, que, al fin y al cabo, es lo único que hemos hecho.
Lo que sí que podemos hacer ahora es usar la relación lineal observada para predecir la altura
media de los niños de otras edades. Por ejemplo, ¿qué altura media estimamos que tienen los
2-4
niños de 10 años? Si aplicamos la regla
Call :
lm ( formula = altura ~ edad , data = datos1 )
Residuals :
Min 1 Q Median 3Q Max
- 4.351 - 1.743 0.408 2.018 2.745
Coefficients :
Estimate Std . Error t value Pr ( >| t |)
( Intercept ) 73.9681 1.7979 41.14 1.38 e - 08 * * *
edad 6.4934 0.2374 27.36 1.58 e - 07 * * *
---
Signif . codes : 0 ’* * * ’ 0.001 ’* * ’ 0.01 ’* ’ 0.05 ’. ’ 0.1 ’ ’ 1
Por ahora podemos prescindir de casi toda esta información (en todo caso, observad que la
columna Estimate nos da los coeficientes de la recta de regresión), y fijarnos sólo en el primer
valor de la penúltima línea, Multiple R-squared. Éste es el coeficiente de determinación R2
que nos interesa. En este caso ha sido de 0.992, lo que confirma que la recta de regresión
aproxima muy bien los datos.
2-5
Podemos pedir a R que nos dé el valor Multiple R-squared sin tener que obtener todo el
summary, añadiendo el sufijo $r.squared a la construcción summary(lm(...)).
> summary ( lm ( altura ~ edad , data = datos1 ) ) $ r . squared
[1] 0.9920466
Los sufijos que empiezan con $ suelen usarse en R para obtener componentes de un objeto.
Por ejemplo, si al nombre de un data frame le añadimos el sufijo formado por $ seguido del
nombre de una de sus variables, obtenemos el contenido de esta variable.
> datos1 $ edad
[1] 1 2 3 5 7 9 11 13
Vamos a usar estos datos para estudiar si hay dependencia lineal entre la altura de un hijo
y la de su padre. Para ello, lo primero que haremos será cargarlos en un data frame. Esto se
puede llevar a cabo de dos maneras:
2-6
File...) o de Internet (From Web Url...); en este ejemplo, hemos de usar la segunda
opción. Al seleccionarla, se nos pide el url del fichero; al entrarlo, se abre una ventana
de diálogo donde podemos especificar el nombre del data frame que queremos crear, si
el fichero tiene o no una primera fila con los nombres de las columnas, cuál es el signo
usado para separar columnas, etc. En el sector «Input File» de esta ventana de diálogo
se puede ver el aspecto del fichero original, y en el sector «Data Frame», el data frame
que obtendremos con las opciones seleccionadas; se trata entonces de escoger las opciones
adecuadas para que se cree la versión correcta del data frame.
En el caso concreto de esta tabla pearson.txt, se tiene que escoger el valor «Yes» en
«Heading» y el valor «Whitespace» en «Separator» (Figura 2.4). Al pulsar el botón
«Import», se importará el fichero en un data frame con el nombre especificado en el
campo «Name» y se verá su contenido en la ventana de ficheros.
Así pues, para cargar esta tabla de datos concreta en un data frame llamado df_pearson,
podemos usar el menú «Import Dataset», o entrar la instrucción siguiente:
> df _ pearson = read . table ( " http : / / aprender . uib . es / Rdir / pearson . txt " ,
header = TRUE )
En ambos casos, para comprobar que se ha cargado bien, podemos usar las funciones str,
que muestra la estructura del data frame, y head, que muestra sus primeras filas.
2-7
> str ( df _ pearson )
’ data . frame ’: 1078 obs . of 2 variables :
$ Padres : num 65 63.3 65 65.8 61.1 ...
$ Hijos : num 59.8 63.2 63.3 62.8 64.3 ...
> head ( df _ pearson )
Padres Hijos
1 65.04851 59.77827
2 63.25094 63.21404
3 64.95532 63.34242
4 65.75250 62.79238
5 61.13723 64.28113
6 63.02254 64.24221
El resultado de str(df_pearson) nos dice que este data frame está formado por 1078 ob-
servaciones (filas) de dos variables (columnas) llamadas Padres e Hijos. El resultado de
head(df_pearson) nos muestra sus primeras seis filas, que podemos comprobar que coinci-
den con las del fichero original mostrado en la Figura 2.3.
Ejecutamos ahora las siguientes instrucciones:
> plot ( df _ pearson )
> lm ( Hijos ~ Padres , data = df _ pearson )
Call :
lm ( formula = Hijos ~ Padres , data = df _ pearson )
Coefficients :
( Intercept ) Padres
33.8866 0.5141
2-8
75
75
70
70
Hijos
Hijos
65
65
60
60
60 65 70 75 60 65 70 75
Padres Padres
entre cada par de marcas consecutivas en el eje de abscisas hay una diferencia de 2 años y
entre cada par de marcas consecutivas en el eje de ordenadas hay una diferencia de 20 cm.
Decimos entonces que los ejes están en escala lineal. Pero a veces es conveniente dibujar algún
eje en escala logarítmica, situando las marcas de tal manera que la misma distancia entre
marcas signifique el mismo cociente entre sus valores. Como el logaritmo transforma cocientes
en restas, un eje en escala logarítmica representa el logaritmo de sus valores en escala lineal.
Decimos que un gráfico está en escala semilogarítmica cuando su eje de abscisas está en escala
lineal y su eje de ordenadas en escala logarítmica. Salvo por los valores en las marcas sobre
el eje de las y, esto significa que dibujamos en escala lineal el gráfico de log(y) en función
de x. Así pues, si al representar unos puntos (x, y) en escala semilogarítmica observamos que
siguen aproximadamente una recta, esto querrá decir que los valores log(y) siguen una ley
aproximadamente lineal en los valores x, y, por lo tanto, que y sigue una ley aproximadamente
exponencial en x. En efecto, si log(y) = ax + b, entonces
donde = 10b .
Veamos algunos ejemplos de regresiones lineales con cambios de escala.
2-9
Ejemplo 2.2. La serotonina se asocia a la estabilidad emocional en el hombre. En un cierto
experimento2 se midió, para algunas cantidades de serotonina, el porcentaje de inhibición de
un cierto proceso bioquímico en el que se observaba su presencia. El objetivo era estimar la
cantidad de serotonina presente en un tejido a partir del porcentaje de inhibición observado.
Los datos que se obtuvieron son los de la Tabla 2.2.3
obtenemos la Figura 2.6, donde vemos claramente que la cantidad de serotonina no es función
lineal de la inhibición.
30
25
20
ser
15
10
5
0
20 30 40 50 60 70 80
inh
Vamos a dibujar ahora el gráfico semilogarítmico de estos puntos, para ver si de esta manera
quedan sobre una recta. Para ello, tenemos que añadir al argumento de plot el parámetro
log="y":
2
Véase el artículo «Serotonin: Radioimmunoassay» de B. Peskar y S. Spector (Science 179, 1973, pp. 1340-
1341).
3
ng es la abreviatura de nanogramo, la milmillonésima parte de un gramo.
2-10
> plot ( inh , ser , log = " y " )
Los puntos en este gráfico sí que parecen seguir una recta. Por lo tanto, parece que el logaritmo
de la cantidad de serotonina es una función aproximadamente lineal del porcentaje de inhibición.
Para confirmarlo, calcularemos la recta de regresión de los puntos
Para calcular los logaritmos en base 10 de todas las cantidades de serotonina en un solo paso,
podemos aplicar la función log10 directamente al vector ser.
> log10 ( ser )
[1] 0.07918125 0.55630250 1.07918125 1.51851394
> lm ( log10 ( ser ) ~ inh )
Call :
lm ( formula = log10 ( ser ) ~ inh )
Coefficients :
( Intercept ) inh
- 0.28427 0.02196
El resultado indica que la recta de regresión de estos puntos es y = 0.02196x 0.28427, con
un valor de R2 de 0.992, muy bueno. Por lo tanto, podemos afirmar que, aproximadamente,
2-11
Elevando 10 a cada uno de los lados de esta identidad, obtenemos
y = 0.52 · 1.052x .
Vamos ahora a dibujar en un mismo gráfico los puntos (inhibiciónn , serotoninan ) y esta función
exponencial. Para añadir la gráfica de una función y = f (x) al gráfico activo en la pestaña
Plots podemos emplear la función
curve(f (x), add=TRUE).
Así,
> plot ( inh , ser )
> curve (0.52 * 1.052^ x , add = TRUE )
produce la Figura 2.8; fijaos en cómo hemos especificado la función y = 0.52 · 1.052x dentro del
curve.
30
25
20
ser
15
10
5
0
20 30 40 50 60 70 80
inh
para estimar la cantidad de serotonina presente en el tejido a partir de una inhibición concreta.
Por ejemplo, si hemos observado un 25 % de inhibición, podemos estimar que la cantidad de
serotonina será
0.52 · 1.05225 = 1.84 ng.
2-12
Ejemplo 2.3. Consideremos ahora los datos de la Tabla 2.3. Se trata de los números acumu-
lados de casos de SIDA en los Estados Unidos desde 1981 hasta 1992.4 Acumulados significa
que, para cada año, se da el número de casos detectados hasta entonces.
Obtenemos el gráfico de la izquierda de la Figura 2.9, y está claro que los puntos (xn , yn ), donde
x representa el año e y el número acumulado de casos de SIDA, no se ajustan a una recta. De
hecho, a simple vista se diría que el crecimiento de y en función de x es exponencial.
Para confirmar este crecimiento exponencial, dibujamos el gráfico semilogarítmico:
> plot ( df _ SIDA , log = " y " )
produce el gráfico central de la Figura 2.9, donde los puntos tampoco siguen una recta, y, por
lo tanto, y tampoco es función exponencial de x.
Vamos a ver si el crecimiento de y en función de x es potencial. Para ello, dibujaremos un
gráfico doble logarítmico de los puntos (xn , yn ), especificando log="xy" dentro del argumento
de plot.
> plot ( df _ SIDA , log = " xy " )
2-13
250000
5e+04
5e+04
200000
150000
SIDA.acum
SIDA.acum
SIDA.acum
5e+03
5e+03
100000
5e+02
5e+02
50000
1e+02
1e+02
0
2 4 6 8 10 12 2 4 6 8 10 12 1 2 5 10
Call :
lm ( formula = log10 ( SIDA _ acum ) ~ log10 ( tiempo ) , data = df _ SIDA )
Coefficients :
( Intercept ) log10 ( tiempo )
1.918 3.274
La regresión que obtenemos es log(y) = 1.918 + 3.274 log(x), con un valor de R2 de 0.998,
muy alto. Elevando 10 a ambos lados de esta igualdad, obtenemos
Para ver si los puntos (tiempon , SIDA_acumn )n=1,...,12 se ajustan bien a la curva
y = 82.79422 · x3.274 ,
produce la Figura 2.10, donde vemos que la curva se ajusta bastante bien a los puntos.
Hay que mencionar aquí que se han propuesto modelos matemáticos5 que predicen que,
cuando se inicia una epidemia de SIDA en una población, los números acumulados de casos
5
Véase el artículo «Risk behavior-based model of the cubic growth of acquired immunodeficiency syndrome in
the United States» de S. A. Colgate, E. A. Stanley, J. M. Hyman, S. P. Layne y C. Qualls (Proc. Natl. Acad.
Sci. USA 86, 1989, pp. 4793–4797).
2-14
250000
200000
150000
SIDA.acum
100000
50000
0
2 4 6 8 10 12
tiempo
en los primeros años son proporcionales al cubo del tiempo transcurrido desde el inicio. El
resultado del análisis que hemos realizado es consistente con esta predicción teórica.
2-15
abline añade una recta al gráfico activo.
2.4. Ejercicio
Las larvas de Lymantria dispar, conocidas como orugas peludas del alcornoque, son una plaga
en bosques y huertos. En un experimento6 se quiso determinar la capacidad de atracción de
una cierta feromona sobre los machos de esta especie, con el objetivo de emplearla en trampas.
En la Tabla 2.4, x representa la cantidad de feromona empleada, en µg,7 y N el número de
machos atrapados en una trampa empleando esta cantidad de feromona para atraerlos.
x 0.1 1 5 10 100
N 3 6 9 11 20
Tabla 2.4. Cantidades de feromona empleadas en trampas y
números de machos atrapados.
(a) Decidid si, en los puntos (x, N ) dados en la Tabla 2.4, el valor de N sigue una función
aproximadamente lineal, exponencial o potencial en el valor de x.
(b) En caso de ser una función de uno de estos tres tipos, calculadla.
(c) Representad en un gráfico los puntos (x, N ) de la Tabla 2.4 y la función que hayáis calculado
en el apartado anterior, para visualizar la bondad del ajuste de la curva a los puntos.
(d) Estimad cuánta feromona tenemos que usar en una trampa para atraer a 50 machos.
6
Véase el artículo «Gypsy moth control with the sex attractant pheromone» de M. Beroza y E. F. Knipling
(Science 177, 1972, pp. 19–27).
7
µg es la abreviatura de microgramo, la millonésima parte de un gramo.
2-16
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 2
Lección 3
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 3
Vectores y otros tipos de listas
Un vector es una secuencia ordenada de datos. R dispone de muchos tipos de datos, entre los
que destacamos:
character (palabras)
Una restricción fundamental de los vectores en R es que todos sus objetos han de ser del mismo
tipo: todos números, todos palabras, etc. Cuando queramos usar vectores formados por objetos
de diferentes tipos, tendremos que usar listas heterogéneas, lists en el argot de R (véase la
Sección 3.5).
1, 5, 6, 2, 5, 7, 8, 3, 5, 2, 1, 0,
Si queremos crear un vector de palabras con la instrucción c, tenemos que entrarlas obliga-
toriamente entre comillas. R también nos las muestra entre comillas.
> nombres = c ( " Pep " ," Catalina " ," Joan " ," Pau " )
> nombres
[1] " Pep " " Catalina " " Joan " " Pau "
> nombres = c ( Pep , Catalina , Joan , Pau ) # Si nos olvidamos de las
comillas ...
Error : object ’ Pep ’ not found
Hemos mencionado que todos los elementos de un vector han de ser del mismo tipo. Por
este motivo, si concatenamos datos de diferentes tipos en un vector, R automáticamente los
convertirá a un tipo que pueda ser común a todos ellos. El orden de conversión entre los tipos
que hemos explicado al principio de la lección es: character gana a complex, que gana a
numeric, que gana a integer, que gana a logical. Así, cuando alguna entrada de un vector
es de tipo palabra, R considera el resto de sus entradas como palabras (y las muestra entre
comillas), como se puede ver en el siguiente ejemplo:
> c (2 ,3.5 , TRUE , " casa " )
[1] " 2 " " 3.5 " " TRUE " " casa "
Otra posibilidad para crear un vector es usar la función scan. Si ejecutamos la instrucción
scan() (así, con el argumento vacío), R abre en la consola un entorno de diálogo donde podemos
ir entrando datos separados por espacios en blanco; cada vez que pulsemos la tecla Entrar, R
importará los datos que hayamos escrito desde la vez anterior en que la pulsamos y abrirá una
nueva línea donde esperará más datos; cuando hayamos acabado, dejamos la última línea en
blanco (pulsando por última vez la tecla Entrar) y R cerrará el vector.
Por ejemplo, para crear un vector x_scan que contenga dos copias de
1 5 6 2 5 7 8 3 5 2 1 0,
La función scan también se puede usar para copiar en un vector el contenido de un fichero
de texto situado en el directorio de trabajo, o del que conozcamos su dirección en Internet.
La manera de hacerlo es aplicando scan al nombre del fichero o a su url, entrados en ambos
casos entre comillas. Por ejemplo, para definir un vector llamado notas con las notas de un
examen que tenemos guardadas en el fichero http://aprender.uib.es/Rdir/notas.txt, sólo
tenemos que entrar:
> notas = scan ( " http : / / aprender . uib . es / Rdir / notas . txt " )
Read 65 items
> notas
[1] 4.1 7.8 5.8 6.5 4.8 6.9 1.3 6.4 4.6 6.9 9.4
[12] 3.0 6.8 4.8 5.6 7.7 10.0 4.4 1.7 8.0 6.3 3.0
[23] 7.5 3.8 7.2 5.7 7.3 6.0 5.7 4.7 5.1 1.5 7.0
[34] 7.0 6.0 6.6 7.2 5.0 3.5 3.3 4.7 5.4 7.1 8.2
[45] 6.7 0.1 5.1 6.8 6.9 8.8 4.5 6.6 2.0 3.0 6.7
[56] 7.9 7.7 6.4 3.0 5.3 5.1 5.3 5.1 5.4 3.0
3-2
> notas2 = scan ( " notas . txt " )
Read 65 items
> notas2
[1] 4.1 7.8 5.8 6.5 4.8 6.9 1.3 6.4 4.6 6.9 9.4
[12] 3.0 6.8 4.8 5.6 7.7 10.0 4.4 1.7 8.0 6.3 3.0
[23] 7.5 3.8 7.2 5.7 7.3 6.0 5.7 4.7 5.1 1.5 7.0
[34] 7.0 6.0 6.6 7.2 5.0 3.5 3.3 4.7 5.4 7.1 8.2
[45] 6.7 0.1 5.1 6.8 6.9 8.8 4.5 6.6 2.0 3.0 6.7
[56] 7.9 7.7 6.4 3.0 5.3 5.1 5.3 5.1 5.4 3.0
sep: sirve para indicar el signo usado para separar entradas consecutivas si no son espacios
en blanco. Para ello se ha de igualar sep a este signo, entrecomillado. Por ejemplo, si
vamos a entrar las entradas separadas por comas (o si están así en el fichero que vamos
a importar), tenemos que especificar sep=",".
> x _ scan2 = scan ()
1: 1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5
1:
Error in scan ( file , what , nmax , sep , dec , quote , skip , nlines ,
na . strings , : scan () expected ’a real ’ , got ’ 1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5 ’
> x _ scan2 = scan ( sep = " ," )
1: 1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5
13:
Read 12 items
> x _ scan2
[1] 1 5 6 2 5 7 8 3 5
dec: sirve para indicar el separador decimal cuando no es un punto. Para ello hemos
de igualar dec al separador decimal entre comillas. Por ejemplo, si queremos crear con
scan un vector formado por los dos números reales 4,5 y 6,2 escritos exactamente de esta
manera, tenemos que especificar dec=",".
3-3
Read 2 items
> x _ scan3
[1] 4.5 6.2
what: sirve para indicar a R de qué tipo tiene que considerar los datos que se le entren. En
particular, what="character" especifica que los valores que se van a entrar en la consola
o el fichero son palabras, aunque no estén entre comillas (si se entran entre comillas, no
hace falta especificarlo).
encoding: sirve para indicar la codificación de alfabeto del fichero externo que se va a
importar. Sólo es necesario especificarlo si dicho fichero contiene caracteres que no sean de
7 bits; o sea, letras acentuadas o caracteres especiales. En este caso, si su codificación no es
la que espera nuestro ordenador y no la especificamos con este parámetro, estos caracteres
se importarán mal. Los dos valores posibles que podemos darle son "latin1" y "UTF-8".
Por ejemplo, si sois usuarios de Windows, seguramente vuestro ordenador espere que el
fichero a importar esté codificado en latin1; entonces, si está codificado en utf8 y contiene
letras acentuadas, no las entenderá a no ser que especifiquéis encoding="UTF-8". Los
ficheros que usaremos en este curso estarán codificados en utf8, pero no contendrán letras
acentuadas ni caracteres especiales, por lo que no será necesario usar este parámetro.
Veamos un ejemplo en sentido contrario: en http://aprender.uib.es/Rdir/enlatin1.
txt hemos guardado algunos nombres con acentos y hemos codificado el fichero en latin1.
En la sesión siguiente se puede ver cómo si lo importamos desde un ordenador Mac sin
avisar de la codificación, los acentos se traducen mal.
> x _ scan5 = scan ( " http : / / aprender . uib . es / Rdir / enlatin1 . txt " , what = "
character " )
Read 3 items
> x _ scan5
[1] " Juan " " Mar \ xeda " " Jos \ xe9 "
> x _ scan6 = scan ( " http : / / aprender . uib . es / Rdir / enlatin1 . txt " , what = "
character " , encoding = " latin1 " )
Read 3 items
> x _ scan6
[1] " Juan " " Mar í a " " Jos é "
Para definir un vector constante podemos usar la función rep(a, n), que genera un vector
que contiene el valor a repetido n veces.
3-4
> rep (1 , 6)
[1] 1 1 1 1 1 1
> rep ( " Palma " , 5) # Las palabras , siempre entre comillas
[1] " Palma " " Palma " " Palma " " Palma " " Palma "
La función rep también se puede usar para repetir vectores. Ahora bien, cuando decimos que
queremos repetir cinco veces los valores 1, 2, 3, podemos referirnos a una de las dos construc-
ciones siguientes:
1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 o 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3.
Para especificar el tipo de repetición tenemos que usar el parámetro adecuado en el argumento
de rep: si añadimos times=5, repetiremos el vector en bloque cinco veces (en el primer sentido),
y si en cambio añadimos each=5, repetiremos cada valor cinco veces (en el segundo sentido).
> rep ( c (1 ,2 ,3) , times =5)
[1] 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
> rep ( c (1 ,2 ,3) , each =5)
[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
Las progresiones aritméticas se pueden especificar de manera compacta usando la función seq.
Una primera manera de hacerlo es mediante la instrucción seq(a, b, by=p), que especifica la
progresión aritmética de paso p que empieza en a,
a, a + p, a + 2p, . . . ,
hasta llegar a b. En concreto, si a < b y p > 0, la función seq(a, b, by=p) genera un vector
con la secuencia creciente a, a+p, a+2p, . . . , hasta llegar al último valor de esta sucesión menor
o igual que b.
> seq (3 , 150 , by =4.5)
[1] 3.0 7.5 12.0 16.5 21.0 25.5 30.0 34.5 39.0
[10] 43.5 48.0 52.5 57.0 61.5 66.0 70.5 75.0 79.5
[19] 84.0 88.5 93.0 97.5 102.0 106.5 111.0 115.5 120.0
[28] 124.5 129.0 133.5 138.0 142.5 147.0
Si a > b y p < 0, entonces seq(a, b, by=p) genera un vector con la secuencia decreciente
a, a + p, a + 2p, . . . , hasta parar en el último valor de esta sucesión mayor o igual que b.
> seq (80 , 4 , by = - 3.5)
[1] 80.0 76.5 73.0 69.5 66.0 62.5 59.0 55.5 52.0 48.5 45.0
[12] 41.5 38.0 34.5 31.0 27.5 24.0 20.5 17.0 13.5 10.0 6.5
3-5
Si el signo de p no es el correcto, obtenemos un mensaje de error.
> seq (80 , 4 , by =3.5)
Error in seq . default (80 , 4 , by = 3.5) : wrong sign in ’ by ’ argument
Como vimos en la lección anterior, la instrucción seq con paso ±1 se puede abreviar con el
signo «:». La instrucción a:b define la secuencia de números consecutivos entre dos números a
y b, es decir, la secuencia a, a + 1, a + 2, . . . hasta llegar a b (si a < b), o a, a 1, a 2, . . . hasta
llegar a b (si a > b).
> 1:15
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> 2.3:12.5
[1] 2.3 3.3 4.3 5.3 6.3 7.3 8.3 9.3 10.3 11.3 12.3
> 34: - 5
[1] 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
[20] 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - 1 - 2 - 3
[39] - 4 - 5
> - 3:5 # Cuidado con los par é ntesis
[1] - 3 - 2 - 1 0 1 2 3 4 5
> - (3:5)
[1] - 3 - 4 - 5
La función seq también se puede usar para definir progresiones aritméticas de otras dos
maneras:
a, a + p, a + 2p, . . . , a + (n 1)p
A estas alturas habréis observado que cuando el resultado de una instrucción es un vector, R
comienza cada línea del resultado con un número entre corchetes [ ]. Este número indica la
posición dentro del vector de la primera entrada de la línea correspondiente. De esta manera, en
el resultado de seq(2, 10, length.out=10), R nos indica que 2.000000 es el primer elemento
de este vector y 6.444444 su sexto elemento.
La función c que hemos usado para crear vectores en realidad concatena sus argumentos en
un vector (de ahí viene la c). Si la aplicamos a vectores, crea un nuevo vector concatenando
sus elementos. Podemos mezclar vectores y datos en su argumento.
3-6
> x = c ( rep (1 , 10) , 2:10)
> x
[1] 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 10
> x = c (0 ,x ,20 ,30)
> x
[1] 0 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9
[20] 10 20 30
Esta última construcción, x=c(0,x,20,30), muestra que la función c se puede usar para añadir
valores al principio o al final de un vector sin cambiarle el nombre: en este caso, hemos redefinido
x añadiéndole un 0 al principio y 20, 30 al final.
Un vector se puede modificar fácilmente usando el editor de datos que incorpora RStudio.
Para hacerlo, se aplica la función fix al vector que queremos editar. R abre entonces el vector
en una nueva ventana de edición. Mientras esta ventana esté abierta, será la ventana activa de
R y no podremos volver a nuestra sesión de R hasta que la cerremos. Los cambios que hagamos
en el vector con el editor de datos se guardarán cuando cerremos esta ventana.
Probadlo. Cread un vector con R y abridlo en el editor. Por ejemplo:
> x = c ( rep (1 , 10) , 2:10)
> fix ( x )
Se abrirá entonces una ventana como la que mostramos en la Figura 3.1. Ahora, en esta ventana,
podéis añadir, borrar y cambiar los datos que queráis. Por ejemplo, añadid un 0 al principio y
20, 30 al final y guardad el resultado (pulsando «Save» en la ventana del editor). El valor de x
se habrá modificado, como podréis comprobar entrando x en la consola.
3-7
[1] 2 5 8 11 14 17 20 23 26 29
> x +2.5
[1] 4.5 7.5 10.5 13.5 16.5 19.5 22.5 25.5 28.5 31.5
> 2.5 * x
[1] 5.0 12.5 20.0 27.5 35.0 42.5 50.0 57.5 65.0 72.5
> sqrt ( x )
[1] 1.414214 2.236068 2.828427 3.316625 3.741657 4.123106 4.472136
[8] 4.795832 5.099020 5.385165
> 2^ x
[1] 4 32 256 2048 16384 131072
[7] 1048576 8388608 67108864 536870912
> x ^2
[1] 4 25 64 121 196 289 400 529 676 841
> (1:4) ^2
[1] 1 4 9 16
> 1:4^2 # Cuidado con los par é ntesis
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
A veces no es posible aplicar una función concreta a todo un vector entrándolo dentro del
argumento de la función, como hemos hecho en los ejemplos anteriores. En estos casos, podemos
usar la instrucción
sapply(vector , FUN=función).
Por ejemplo, dentro de un rato veremos que la función mean calcula la media aritmética de un
vector. Supongamos que definimos una función F que, aplicada a un número natural x, calcula
la media de los números
1, 2, . . . , x.
Resulta que no podemos aplicar esta función a todas las entradas de un vector x entrando
simplemente F(x).
> F (20:30)
[1] 10.5
Warning message :
In 1: x : numerical expression has 11 elements : only the first used
También podemos operar término a término las entradas de dos vectores de la misma longitud.
> 1:5+1:5 # Suma entrada a entrada
[1] 2 4 6 8 10
3-8
> (1:5) * (1:5) # Producto entrada a entrada
[1] 1 4 9 16 25
> (1:5) ^(1:5) # Potencia entrada a entrada
[1] 1 4 27 256 3125
Esto nos permite calcular fácilmente vectores de la forma (xn )n=p,...,q , formados por los térmi-
nos xp , xp+1 , . . . , xq de una sucesión (xn )n , a partir de la fórmula explícita de xn como función
del índice n: basta aplicar esta fórmula a p:q. Por ejemplo, para definir el vector
x = (3 · 2n 20)n=1,...,20 ,
3-9
max y min calculan sus valores máximo y mínimo, respectivamente.
diff calcula el vector formado por las diferencias sucesivas entre entradas del vector
original.
cumsum calcula el vector formado por las sumas acumuladas de las entradas del vector
original: cada entrada de cumsum(x) es la suma de las entradas de x hasta su posición.
sort ordena los elementos del vector en el orden natural creciente del tipo de datos que
lo forman: el orden numérico, el orden alfabético, etc. Si lo queremos ordenar en orden
decreciente, podemos incluir en su argumento el parámetro dec=TRUE.
rev invierte el orden de los elementos del vector; por lo tanto, rev(sort(...)) es otra
opción para ordenar en orden decreciente.
> x = c (1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5 ,2 ,1 ,0)
> length ( x )
[1] 12
> max ( x )
[1] 8
> min ( x )
[1] 0
> sum ( x )
[1] 45
> prod ( x )
[1] 0
> mean ( x )
[1] 3.75
> cumsum ( x )
[1] 1 6 12 14 19 26 34 37 42 44 45 45
> diff ( x )
[1] 4 1 - 4 3 2 1 - 5 2 - 3 - 1 - 1
> sort ( x )
[1] 0 1 1 2 2 3 5 5 5 6 7 8
> sort (x , dec = TRUE )
[1] 8 7 6 5 5 5 3 2 2 1 1 0
> rev ( x )
[1] 0 1 2 5 3 8 7 5 2 6 5 1
La función sum es útil para evaluar sumatorios; por ejemplo, si queremos calcular
200
X 1
,
n=0
n2 +1
3-10
> n =0:200
> sum (1 / ( n ^2+1) )
[1] 2.071687
La función cumsum permite definir sucesiones descritas mediante sumatorios; a modo de ejem-
plo, para definir el vector
⇣X n ⌘
i
y= 2 ,
n=0,...,20
i=0
3-11
[1] 42.5
vector [y], donde y es un vector (de índices), crea un nuevo vector con las entradas del
vector original cuyos índices pertenecen a y.
En particular, si a y b son dos números naturales, vector [a:b] crea un nuevo vector con
las entradas del vector original que van de la a-ésima a la b-ésima.
vector [-y], donde y es un vector (de índices), es el complementario de vector [y]: sus
entradas son las del vector original cuyos índices no pertenecen a y.
En particular, vector [-i] borra la entrada i-ésima del vector original.
También podemos extraer las entradas de un vector (o sus índices) que satisfagan alguna
condición. Los operadores lógicos que podemos usar para definir estas condiciones son los que
damos en la Tabla 3.1. Veamos algunos ejemplos (y observad su sintaxis):
3-12
Operador = 6= < > 6 > negación conjunción disjunción
Signo == != < > <= >= ! & |
> x = c (1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5 ,2 ,1 ,0)
> x [x >3] # Elementos mayores que 3
[1] 5 6 5 7 8 5
> x [x >2 & x < =5] # Elementos mayores que 2 y menores o iguales que 5
[1] 5 5 3 5
> x [ x ! = 2 & x ! = 5] # Elementos diferentes de 2 y de 5
[1] 1 6 7 8 3 1 0
> x [x >5 | x < =2] # Elementos mayores que 5 o menores o iguales que 2
[1] 1 6 2 7 8 2 1 0
> x [x >=4] # Elementos mayores o iguales que 4
[1] 5 6 5 7 8 5
> x [ ! x < 4] # Elementos que NO son menores que 4; es equivalente a la
anterior
[1] 5 6 5 7 8 5
> x [ x % %4==0] # Elementos m ú ltiplos de 4
[1] 8 0
Analicemos la segunda instrucción, x[x>3]. La construcción x>3 define un vector que, en cada
posición, contiene un TRUE si el elemento correspondiente del vector x es mayor que 3 y un
FALSE si no lo es.
> x >3
[1] FALSE TRUE TRUE FALSE TRUE TRUE TRUE FALSE TRUE
[10] FALSE FALSE FALSE
Entonces x[x>3] lo que nos da son las entradas del vector x correspondientes a los TRUE de
este vector de valores lógicos.
> x [x >3]
[1] 5 6 5 7 8 5
Esta construcción también permite extraer las entradas de un vector cuyos índices sean los
de las entradas de otro vector que satisfagan una condición lógica. Por ejemplo:
> x = c (1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5 ,2 ,1 ,0)
> y = c (2 , - 3 ,0 ,1 ,2 , - 1 ,4 , - 1 , - 2 ,3 ,5 ,1)
> x [y >0] # Entradas de x correspondientes a entradas positivas de y
[1] 1 2 5 8 2 1 0
Para obtener los índices de las entradas del vector que satisfacen una condición dada, podemos
usar la función which. Esta función, aplicada a un vector de valores lógicos, da los índices de
las posiciones de los TRUE. Así, para saber los índices de las entradas de x que son mayores que
3, usamos which(x>3), que nos dará los índices de las entradas TRUE del vector x>3.
> x = c (1 ,5 ,6 ,2 ,5 ,7 ,8 ,3 ,5 ,2 ,1)
3-13
> x
[1] 1 5 6 2 5 7 8 3 5 2 1
> x [x >3] # Elementos mayores que 3
[1] 5 6 5 7 8 5
> which (x >3) # Í ndices de los elementos mayores que 3
[1] 2 3 5 6 7 9
> which (x >2 & x < =5) # Í ndices de los elementos > 2 y < = 5
[1] 2 5 8 9
> which ( x ! = 2 & x ! = 5) # Í ndices de los elementos diferentes de 2 y 5
[1] 1 3 6 7 8 11
> which (x >5 | x < =2) # Í ndices de los elementos > 5 o < = 2
[1] 1 3 4 6 7 10 11
> which ( x % %2==0) # Í ndices de los elementos pares del vector
[1] 3 4 7 10
La instrucción which.min(x) nos da la primera posición en la que el vector toma su valor míni-
mo; which.max(x) hace lo mismo, pero para el máximo. En cambio, con which(x==min(x)) ob-
tenemos todas las posiciones en las que el vector toma su valor mínimo y, con which(x==max(x)),
aquellas en las que toma su valor máximo.
> x
[1] 1 5 6 2 5 7 8 3 5 2 1
> which . min ( x )
[1] 1
> which ( x == min ( x ) )
[1] 1 11
Si un vector no contiene ningún término que satisfaga la condición que imponemos, obtenemos
como respuesta un vector vacío. R lo indica con numeric(0) si es de números, character(0)
si es de palabras, o integer(0) si es de índices de entradas de un vector. Estos vectores vacíos
tienen longitud, naturalmente, 0.
> x =2^(0:10)
> x
[1] 1 2 4 8 16 32 64 128 256 512 1024
> x [20 < x & x < 30] # Elementos de x estrictamente entre 20 y 30
numeric (0)
> length ( x [20 < x & x < 30]) # ¿ Cu á ntas entradas hay entre 20 y 30?
[1] 0
> which (x >1500) # Í ndices de elementos mayores que 1500
integer (0)
Si R no sabe de qué tipo son los datos que faltan en un vector vacío, lo indica con NULL.
También podemos usar este valor para definir un vector vacío.
> x = c ()
> x
NULL
> z = NULL
> z
NULL
3-14
> y = c (x , 2 , z )
> y
[1] 2
Los operadores lógicos que hemos explicado también se pueden usar para pedir si una condi-
ción sobre unos números concretos se satisface o no. Por ejemplo:
> exp ( pi ) > pi ^( exp (1) ) # ¿ Es mayor e ^ pi que pi ^ e ?
[1] TRUE
> 1234567 % %9==0 # ¿ Es 1234567 m ú ltiplo de 9?
[1] FALSE
Podemos modificar algunas entradas de un vector simplemente declarando sus nuevos valores.
Esto se puede hacer entrada a entrada, o para todo un subvector de golpe.
> x =1:10
> x
[1] 1 2 3 4 5 6 7 8 9 10
> x [3]=15 # En la posici ó n 3 escribimos 15
> x [11]=25 # A ñ adimos en la posici ó n 11 un 25
> x
[1] 1 2 15 4 5 6 7 8 9 10 25
> x [ c (2 , 3 , 4) ]= x [ c (2 , 3 , 4) ]+10 # Sumamos 10 a las entradas en las
posiciones 2 , 3 y 4
> x
[1] 1 12 25 14 5 6 7 8 9 10 25
> x [( length ( x ) - 2) : length ( x ) ]=0 # Igualamos las ú ltimas tres
entradas a 0
> x
[1] 1 12 25 14 5 6 7 8 0 0 0
> x [ length ( x ) +3]=2
> x
[1] 1 12 25 14 5 6 7 8 0 0 0 NA NA 2
Fijaos en la última instrucción. Hemos añadido al vector x el valor 2 tres posiciones más allá
de su última entrada. Entonces, en las posiciones 12 y 13 ha escrito NA antes de añadir en la 14
el 2. Estos NA, de Not Available, indican que las entradas correspondientes del vector no existen.
Los NA serán muy importantes cuando usemos vectores en estadística descriptiva, donde
podrán representar valores desconocidos, errores, etc. Serán importantes porque son molestos,
puesto que, por norma general, una función aplicada a un vector que contenga algún NA da NA.
> sum ( x )
[1] NA
> mean ( x )
[1] NA
Afortunadamente, muchas de las funciones para vectores admiten un parámetro na.rm que,
igualado a TRUE, hace que la función sólo tenga en cuenta las entradas definidas.
> sum (x , na . rm = TRUE )
[1] 80
3-15
> mean (x , na . rm = TRUE )
[1] 6.666667
Para extraer las entradas no definidas de un vector x no podemos usar la condición lógica
x==NA, sino la función is.na(x).
> x
[1] 1 12 25 14 5 6 7 8 0 0 0 NA NA 2
> which ( x == NA ) # ¿ Í ndices de entradas NA ?
integer (0)
> is . na ( x )
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[10] FALSE FALSE TRUE TRUE FALSE
> which ( is . na ( x ) ) # Í ndices de entradas NA
[1] 12 13
> y = x # Creamos una copia de x y la llamamos y
> y [ is . na ( y ) ]= mean (y , na . rm = TRUE ) # Cambiamos los NA de y por la
media del resto de entradas
> y
[1] 1.000000 12.000000 25.000000 14.000000 5.000000
[6] 6.000000 7.000000 8.000000 0.000000 0.000000
[11] 0.000000 6.666667 6.666667 2.000000
Naturalmente, podemos usar la negación de is.na(x) para obtener las entradas definidas de
un vector x: formarán el vector x[!is.na(x)].
> x
[1] 1 12 25 14 5 6 7 8 0 0 0 NA NA 2
> x [ ! is . na ( x ) ]
[1] 1 12 25 14 5 6 7 8 0 0 0 2
> sum ( x [ ! is . na ( x ) ])
[1] 80
> cumsum ( x )
[1] 1 13 38 52 57 63 70 78 78 78 78 NA NA NA
> cumsum (x , na . rm = TRUE ) # cumsum no admite na . rm
Error in cumsum (x , na . rm = TRUE ) :
2 arguments passed to ’ cumsum ’ which requires 1
> cumsum ( x [ ! is . na ( x ) ])
[1] 1 13 38 52 57 63 70 78 78 78 78 80
3-16
[1] 1 13 38 52 57 63 70 78 78 78 78 80
Observad el resultado de na.omit(x). Contiene un primer vector formado por las entradas
del vector original que no son NA, y luego una serie de información extra llamados atributos, e
indicados por R con attr: los índices de las entradas que ha eliminado y el tipo de acción que
ha llevado a cabo. Como podéis ver, estos atributos no interfieren para nada en las operaciones
que se realicen con el primer vector, pero si os molestan, se pueden eliminar: la instrucción
attr(objeto, atributo)=NULL
borra el atributo del objeto.
> x _ sinNA = na . omit ( x )
> x _ sinNA
[1] 1 12 25 14 5 6 7 8 0 0 0 2
attr ( , " na . action " )
[1] 12 13
attr ( , " class " )
[1] " omit "
> attr ( x _ sinNA , " na . action " ) = NULL
> attr ( x _ sinNA , " class " ) = NULL
> x _ sinNA
[1] 1 12 25 14 5 6 7 8 0 0 0 2
3.4. Factores
Un factor es como un vector, pero con una estructura interna más rica que permite usar-
lo para clasificar observaciones. Para ilustrar la diferencia entre vectores y factores, vamos a
crear un vector Ciudades con los nombres de algunas ciudades, y a continuación un factor
Ciudades.factor con el mismo contenido, aplicando a este vector la función factor.
> Ciudades = c ( " Madrid " ," Palma " ," Madrid " ," Madrid " ," Barcelona " ,
" Palma " ," Madrid " ," Madrid " )
> Ciudades
[1] " Madrid " " Palma " " Madrid " " Madrid " " Barcelona "
[6] " Palma " " Madrid " " Madrid "
> Ciudades . factor = factor ( Ciudades )
> Ciudades . factor
[1] Madrid Palma Madrid Madrid Barcelona Palma
[7] Madrid Madrid
Levels : Barcelona Madrid Palma
3-17
como sus niveles los diferentes valores que aparecen en el vector, mientras que factor «define»
un factor a partir del vector, y dispone de algunos parámetros que permiten modificar el factor
que se crea, tales como:
levels, que permite especificar los niveles e incluso añadir niveles que no aparecen en el
vector.
De esta manera, con as.factor o con factor sin especificar levels, el factor tendrá como
niveles los diferentes valores que toman las entradas del vector, y además aparecerán en su lista
de niveles, Levels, ordenados en orden alfabético. Si especificamos el parámetro levels en la
función factor, los niveles aparecerán en dicha lista en el orden en el que los entremos en él.
> S = c ( " M " ," M " ," F " ," M " ," F " ," F " ," F " ," M " ," M " ," F " )
> Sex = as . factor ( S )
> Sex
[1] M M F M F F F M M F
Levels : F M
> Sex2 = factor ( S ) # Esto definir á el mismo factor
> Sex2
[1] M M F M F F F M M F
Levels : F M
> Sex3 = factor (S , levels = c ( " M " ," F " ," B " ) ) # Si queremos a ñ adir un
nuevo nivel " B " y cambiar el orden de los niveles
> Sex3
[1] M M F M F F F M M F
Levels : M F B
> Sex4 = factor (S , levels = c ( " M " ," F " ," B " ) ,
labels = c ( " Masc " ," Fem " ," Bisex " ) ) # Si queremos cambiar los nombres
> Sex4
[1] Masc Masc Fem Masc Fem Fem Fem Masc Masc Fem
Levels : Masc Fem Bisex
3-18
Levels : Muy . mal Mal Bien Muy . bien
Observad que los niveles han heredado el orden del factor original.
Con la función levels también podemos agrupar varios niveles de un factor en uno solo,
simplemente repitiendo nombres al especificarlos; por ejemplo, en el factor de notas anterior,
vamos a agrupar los niveles «Muy mal» y «Mal» en uno solo, y lo mismo con los niveles «Muy
bien» y «Bien»:
> Notas _ 2 niv = Notas
> levels ( Notas _ 2 niv ) = c ( " Mal " ," Mal " ," Bien " ," Bien " )
> Notas _ 2 niv
[1] Mal Mal Mal Bien Mal Bien Mal Bien Mal Bien Bien Mal
Levels : Mal Bien
Nos hemos referido varias veces al orden de los niveles. En realidad, hay dos tipos de factores:
simples y ordenados. Hasta ahora sólo hemos considerado los factores simples, en los que el
orden de los niveles realmente no importa, y si lo modificamos es sólo por razones estéticas o
de comprensión de los datos; en este caso, la manera más sencilla de hacerlo es redefiniendo
el factor con factor y modificando en el parámetro levels el orden de los niveles. Pero si
el orden de los niveles es relevante para analizar los datos, entonces es conveniente definir el
factor como ordenado. Esto se lleva a cabo con la función ordered, que dispone de los mismos
parámetros que factor. Así, si queremos que nuestro factor Notas sea un factor ordenado, con
sus niveles ordenados de «Muy mal» a «Muy bien», hay que entrar lo siguiente:
> Notas = ordered ( Notas , levels = c ( " Muy . mal " ," Mal " ," Bien " ," Muy . bien " ) )
> Notas
[1] Muy . mal Mal Mal Bien Muy . mal Bien Mal
[8] Muy . bien Mal Bien Muy . bien Mal
Levels : Muy . mal < Mal < Bien < Muy . bien
Observad que R indica el orden de los niveles de un factor ordenado mediante el signo <.
Aunque en la instrucción anterior hemos aplicado la función ordered a un factor, también se
puede aplicar a un vector, como si usáramos factor.
3-19
> L
$ nombre
[1] " x "
$ vector
[1] 1 2 - 3 - 4 5 6
$ media
[1] 1.166667
$ sumas
[1] 1 3 0 -4 1 7
Observad la sintaxis de la función list: le hemos entrado como argumento los diferentes
objetos que van a formar la lista heterogénea, poniendo a cada uno un nombre adecuado. Este
nombre es «interno» de la list: por ejemplo, pese a que dentro de la lista hemos definido un
objeto llamado sumas, en el entorno de trabajo de R no tenemos definida ninguna variable con
ese nombre (a no ser que la hayamos definido previamente durante la sesión).
> sumas
Error : object ’ sumas ’ not found
Para obtener una componente concreta de una list, tenemos que añadir al nombre de la
list el sufijo formado por un signo $ y el nombre de la componente; recordad cómo extraíamos
el valor de R2 de un summary(lm(...)) o (en el contexto de los data frames) cómo extraíamos
una columna de un data frame en la página 3-3.
> L $ nombre
[1] " x "
> L $ vector
[1] 1 2 - 3 - 4 5 6
> L $ media
[1] 1.166667
También podemos indicar el objeto por su posición en la list usando un par de dobles
corchetes [[ ]]. Si usamos sólo un par de corchetes, como en los vectores, lo que obtenemos
es una list formada por esa única componente, no el objeto que forma la componente.
> L [[1]]
[1] " x "
> L [[4]] # Esto es un vector
[1] 1 3 0 - 4 1 7
> 3 * L [[4]] # Y podemos operar con é l
[1] 3 9 0 - 12 3 21
> L [4] # Esto es una list , no un vector
$ sumas
[1] 1 3 0 - 4 1 7
> 3 * L [4] # Y NO podemos operar con é l
Error in 3 * L [4] : non - numeric argument to binary operator
3-20
Para conocer la estructura interna de una list, es decir, los nombres de los objetos que la
forman y su naturaleza, podemos usar la función str. Si sólo queremos saber sus nombres,
podemos usar la función names. Si la list se obtiene con una función de R cuyo resultado sea
una estructura de este tipo, como, por ejemplo, lm, es recomendable consultar la Ayuda de la
función, ya que probablemente explique el significado de los objetos que la forman.
> str ( L )
List of 4
$ nombre : chr " x "
$ vector : num [1:6] 1 2 - 3 - 4 5 6
$ media : num 1.17
$ sumas : num [1:6] 1 3 0 - 4 1 7
> names ( L )
[1] " nombre " " vector " " media " " sumas "
rep sirve para definir un vector repitiendo un valor o las entradas de otro vector. Algunos
parámetros importantes:
• each: cuando aplicamos la función a un vector, sirve para indicar cuántas veces
queremos repetir cada entrada del vector.
• times: cuando aplicamos la función a un vector, sirve para indicar cuántas veces
queremos repetir todo el vector en bloque.
seq se puede usar para definir progresiones aritméticas. Algunos parámetros importantes:
a, a + p, a + 2p, . . . , b
3-21
• seq(a, b, length.out=n) define la progresión
a, a + p, a + 2p, . . . , b
a, a + p, a + 2p, . . . , a + (n 1)p.
a:b es sinónimo de seq(a, b, by=1) (si a < b) o seq(a, b, by=-1) (si a > b).
fix abre un vector (o, en general, un objeto de datos: una matriz, un data frame. . . ) en
el editor de datos.
Las funciones max, min, sum, prod y mean admiten el parámetro siguiente:
• na.rm: igualado a TRUE, impone que no se tengan en cuenta los valores NA del vector
al calcularla.
vector [...] se usa para especificar un elemento o un subvector del vector. Las entradas
que formarán el subvector pueden especificarse mediante el vector de sus índices o me-
diante una condición lógica sobre las entradas. Los signos de operadores lógicos que se
pueden usar para definir condiciones lógicas son los siguientes:
which sirve para obtener los índices de las entradas de un vector que satisfacen una
condición lógica.
which.min y which.max dan la primera posición en la que el vector toma su valor mínimo
o máximo, respectivamente.
3-22
as.factor transforma un vector en un factor.
ordered crea un factor ordenado a partir de un vector o un factor; sus parámetros son
los mismos que los de factor.
levels sirve para obtener los niveles de un factor, y también para cambiar sus nombres.
names sirve para conocer los nombres de las componentes de una list.
list[[i]] sirve para referirnos al objeto que forma la i-ésima componente de la list.
3.7. Ejercicio
Tenemos las siguientes notas obtenidas por unos estudiantes en un examen:
7.9, 4.3, 5.5, 7.9, 9.8, 2.7, 4.7, 2.4, 8.3, 7.3, 6.8, 6.3, 4.8, 5.7, 3.8, 6.3, 5.4, 5.4, 80,
4.2, 8.3, 4.7, 6.0, 6.8, 5.7, 6.5, 4.6, 5.4, 3.7, 7.1, 5.5, 6.0, 6.7, 7.0, 7.3, 3.0, 6.6, 6.1,
2.4, 7.1, 9.4, 3.7, 4.5, 5.1, 5.9, 4.7, 5.5, 8.9, 8.1, 8.3, 4.3, 7.1, 9.3, 5.1, 6.1, 3.0, 5.7,
6.8, 3.1, 7.7, 7.3 , 7.0, 6.2, 8.8, 5.3, 4.0.
(a) Cread un vector con estas notas (podéis copiarlas de este documento y pegarlas) y ponedle
un nombre adecuado.
(c) ¡Vaya! El 80 ha sido un error, tenía que ser un 8.0. Cambiad el 80 del vector anterior por
un 8.0, sin volver a entrar el resto de notas. Volved a calcular la media de las notas tras
haber corregido este error.
(d) ¿Cuál es la nota mínima obtenida por estos estudiantes? ¿Cuántos estudiantes la han
sacado?
(e) ¿Cuántos estudiantes han logrado un notable (entre 7 y 8.9)? ¿Qué porcentaje del total de
estudiantes representan?
(f) ¿Qué grupo es más numeroso: el de los estudiantes que han sacado entre 4 y 4.9, o el de
los que han sacado entre 5 y 5.9?
(g) Ordenad en orden creciente estas notas y obtened su mediana: una vez ordenado el vector,
si tiene un número impar de entradas, su mediana es el valor central, y si tiene un número
par de entradas, su mediana es la media aritmética de los dos valores centrales.
3-23
(h) La mediana de un vector se puede calcular directamente con la función median. Calculad
la del vector anterior con esta función. ¿Da lo mismo que el valor obtenido en el punto
anterior?
(i) ¿Cuántos notas diferentes hay en esta muestra? (Podéis emplear astutamente algunas fun-
ciones explicadas en esta lección, o podéis consultar help.search("duplicated") a ver si
encontráis una función que elimine las entradas duplicadas de un vector.)
3-24
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 2
Lección 4
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 4
Matrices
Una matriz de orden n ⇥ m es una tabla rectangular de números (o, en algunas situaciones
específicas, algún otro tipo de datos: valores lógicos, etiquetas. . . ) formada por n filas y m
columnas. Una matriz es cuadrada cuando tiene el mismo número de filas que de columnas, es
decir, cuando n = m; en este caso, decimos que la matriz es de orden n. En matemáticas, es
costumbre escribir las matrices rodeadas por paréntesis para marcar con claridad sus límites.
Por ejemplo, ✓ ◆
2 1 p3
A=
4 3 3
es una matriz 2 ⇥ 3.
Observad cómo muestra R las matrices: indica las filas con [i,], donde i es el índice de la fila,
y las columnas con [,j], donde j es el índice de la columna.
Para construir una matriz de n filas o n columnas, es conveniente que la longitud del vector
al que se aplica matrix sea múltiplo de n. Si no es así, R rellena la última fila o columna con
entradas del principio del vector y emite un mensaje de advertencia.
> matrix (1:6 , nrow =4) # 6 no es m ú ltiplo de 4
[ ,1] [ ,2]
[1 ,] 1 5
[2 ,] 2 6
[3 ,] 3 1
[4 ,] 4 2
Warning message :
In matrix (1:6 , nrow = 4) :
data length [6] is not a sub - multiple or multiple of the number
of rows [4]
En particular, se puede definir una matriz constante aplicando la función matrix a un número.
En este caso, se han de usar los parámetros nrow y ncol para especificar el orden de la matriz.
> matrix (1 , nrow =2 , ncol =3) # Matriz constante 1 de orden 2 x3
[ ,1] [ ,2] [ ,3]
[1 ,] 1 1 1
[2 ,] 1 1 1
Con las funciones cbind o rbind también podemos añadir columnas, o filas, a una matriz;
en concreto, si A es una matriz de orden n ⇥ m y v es un vector de longitud n, la instrucción
cbind(A, v) define la matriz de orden n ⇥ (m + 1) que tiene como primeras m columnas las
de A y como columna m + 1 el vector v; de manera similar, cbind(v, A) define la matriz de
orden n ⇥ (m + 1) que tiene como primera columna el vector v y después las columnas de A.
Con cbind también podemos concatenar por columnas dos matrices con el mismo número de
filas.
La instrucción rbind es similar a cbind, pero actúa por filas en vez de por columnas: permite
añadir filas arriba o abajo de una matriz ya existente, y, en general, concatenar por filas dos
matrices con el mismo número de columnas.
> A = matrix ( c (1 ,2 ,3 ,4) , nrow =2)
4-2
> A
[ ,1] [ ,2]
[1 ,] 1 3
[2 ,] 2 4
> cbind (A , c (5 ,6) )
[ ,1] [ ,2] [ ,3]
[1 ,] 1 3 5
[2 ,] 2 4 6
> cbind ( c (7 ,8) ,c (5 ,6) ,A )
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 7 5 1 3
[2 ,] 8 6 2 4
> cbind (A , A )
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1 3 1 3
[2 ,] 2 4 2 4
> rbind (A , c (10 ,12) )
[ ,1] [ ,2]
[1 ,] 1 3
[2 ,] 2 4
[3 ,] 10 12
> rbind (A , A )
[ ,1] [ ,2]
[1 ,] 1 3
[2 ,] 2 4
[3 ,] 1 3
[4 ,] 2 4
Como pasaba con los vectores, todas las entradas de una matriz en R han de ser del mismo
tipo de datos, y si una matriz contiene datos de diferentes tipos, automáticamente los convierte
a un tipo que pueda ser común a todos ellos.
Si los vectores que concatenamos con rbind o cbind tienen nombres, las filas o columnas
correspondientes de la matriz los heredan.
> x = c (1 ,2 ,3)
> y = c (0 ,1 , - 1)
> rbind (x , y )
[ ,1] [ ,2] [ ,3]
x 1 2 3
y 0 1 -1
> cbind (x , y )
x y
[1 ,] 1 0
[2 ,] 2 1
[3 ,] 3 - 1
Se puede también poner nombres a las filas y las columnas de una matriz con la instrucción
dimnames(matriz )=list(vector de nombres de filas,vector de nombres de columnas).
Si las filas o las columnas no han de tener nombres, se declara su vector de nombres como NULL
en esta list.
4-3
> A = matrix (1:6 , nrow =3)
> A
[ ,1] [ ,2]
[1 ,] 1 4
[2 ,] 2 5
[3 ,] 3 6
> dimnames ( A ) = list ( c ( " X1 " ," X2 " ," X3 " ) ,c ( " Y1 " ," Y2 " ) )
> A
Y1 Y2
X1 1 4
X2 2 5
X3 3 6
> dimnames ( A ) = list ( NULL , c ( " Palma " ," Barcelona " ) )
> A
Palma Barcelona
[1 ,] 1 4
[2 ,] 2 5
[3 ,] 3 6
Los nombres de las filas y columnas de una matriz pueden servir para hacer más clara la
información contenida en la misma.
En los dos últimos casos, el resultado es un vector. Si queremos que el resultado sea una matriz
de una sola fila o de una sola columna, respectivamente, tenemos que añadir el parámetro
drop=FALSE dentro de los corchetes.
> M = matrix ( c (1 ,3 ,5 ,2 ,3 ,6 ,2 ,9 ,8 ,4 ,2 ,5) , nrow =3 , byrow = TRUE )
> M
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1 3 5 2
[2 ,] 3 6 2 9
[3 ,] 8 4 2 5
4-4
> M [2 ,4] # Entrada (2 ,4)
[1] 9
> M [3 ,1] # Entrada (3 ,1)
[1] 8
> M [1 , ] # Fila 1
[1] 1 3 5 2
> M [1 , , drop = FALSE ] # ATENCI Ó N : fijaos en las dos comas
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1 3 5 2
> M [ ,3] # Columna 3
[1] 5 2 2
> M [ ,3 , drop = FALSE ]
[ ,1]
[1 ,] 5
[2 ,] 2
[3 ,] 2
Estas construcciones sirven también para definir submatrices, y no sólo entradas, filas o
columnas. Naturalmente, para indicar más de una fila o más de una columna tenemos que usar
vectores de índices.
> M [ c (1 ,2) ,c (1 ,3) ] # Submatriz de filas 1 , 2 y columnas 1 , 3
[ ,1] [ ,2]
[1 ,] 1 5
[2 ,] 3 2
> M [ c (1 ,3) , ] # Submatriz de filas 1 , 3 y todas las columnas
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1 3 5 2
[2 ,] 8 4 2 5
> M [ ,c (2 ,3 ,4) ] # Submatriz de columnas 2 , 3 , 4 y todas las filas
[ ,1] [ ,2] [ ,3]
[1 ,] 3 5 2
[2 ,] 6 2 9
[3 ,] 4 2 5
Si las filas o las columnas de una matriz tienen nombres, se pueden usar para especificar
trozos de la misma.
> A = matrix (1:9 , nrow =3)
> dimnames ( A ) = list ( c ( " X1 " ," X2 " ," X3 " ) ,c ( " Y1 " ," Y2 " ," Y3 " ) )
> A
Y1 Y2 Y3
X1 1 4 7
X2 2 5 8
X3 3 6 9
> A [ c (1 ,3) ,2]
X1 X3
4 6
> A [ c ( " X1 " ," X3 " ) ," Y2 " ]
X1 X3
4 6
4-5
> A [ c ( " X1 " ," X3 " ) ,c ( " Y1 " ," Y2 " ) ]
Y1 Y2
X1 1 4
X3 3 6
La diagonal principal de una matriz cuadrada (la que va de la esquina superior izquierda a
la esquina inferior derecha) se obtiene con la función diag. Si la matriz no es cuadrada, diag
produce el vector de entradas (1, 1), (2, 2), . . . hasta que se para en la última fila o la última
columna.
> A = matrix (1:9 , nrow =3 , byrow = TRUE )
> A
[ ,1] [ ,2] [ ,3]
[1 ,] 1 2 3
[2 ,] 4 5 6
[3 ,] 7 8 9
> diag ( A )
[1] 1 5 9
> B = matrix (1:10 , nrow =2 , byrow = TRUE )
> B
[ ,1] [ ,2] [ ,3] [ ,4] [ ,5]
[1 ,] 1 2 3 4 5
[2 ,] 6 7 8 9 10
> diag ( B )
[1] 1 7
La mayoría de las funciones numéricas para vectores se pueden aplicar a matrices. Por ejemplo,
podemos usar las funciones sum, prod o mean para obtener la suma, el producto o la media,
respectivamente, de todas las entradas de una matriz.
> A = matrix ( c (1 ,2 ,1 ,3 , - 1 ,3) , nrow =2 , byrow = TRUE )
> A
4-6
[ ,1] [ ,2] [ ,3]
[1 ,] 1 2 1
[2 ,] 3 -1 3
> sum ( A )
[1] 9
> mean ( A )
[1] 1.5
En estadística a veces es necesario calcular la suma o la media por filas o por columnas de
una matriz. Esto se puede llevar a cabo con las instrucciones siguientes:
Si queremos aplicar otras funciones a las filas o las columnas de una matriz, podemos emplear
la función apply. Su estructura básica es
apply(A, MARGIN=..., FUN=función),
donde A es una matriz, la función es la que queremos aplicar, y el valor de MARGIN ha de ser 1 si
la queremos aplicar por filas, 2 si la queremos aplicar por columnas, o c(1, 2) si la queremos
aplicar entrada a entrada; como pasaba con los vectores, en muchas ocasiones podemos aplicar
una función a todas las entradas de una matriz entrando la matriz en su argumento, pero a
veces es necesario usar apply con MARGIN=c(1,2).
Por ejemplo, para calcular la norma euclídea de las filas de la matriz A anterior (la raíz
cuadrada de la suma de los cuadrados de sus entradas), para ordenar cada una de sus columnas,
y para calcular la matriz de raíces cuadradas de las entradas de A, haríamos lo siguiente:
> f = function ( x ) { sqrt ( sum ( x ^2) ) }
> apply (A , MARGIN =1 , FUN = f ) # Normas eucl í deas de filas
[1] 4.242641 6.244998 6.082763
4-7
> A _ ord = apply (A , MARGIN =2 , FUN = sort ) # Matriz con cada columna de A
ordenada
> A _ ord
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1 1 2 1
[2 ,] 2 2 3 2
[3 ,] 4 5 3 4
> sqrt ( A ) # Matriz de ra í ces cuadradas
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1.000000 1.414214 1.732051 1.414214
[2 ,] 1.414214 2.236068 1.732051 1.000000
[3 ,] 2.000000 1.000000 1.414214 2.000000
> apply (A , MARGIN = c (1 ,2) , FUN = sqrt ) # La anterior , usando apply
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1.000000 1.414214 1.732051 1.414214
[2 ,] 1.414214 2.236068 1.732051 1.000000
[3 ,] 2.000000 1.000000 1.414214 2.000000
¡Atención! Si «multiplicáis» dos matrices con el signo *, no obtenéis el producto de las dos
matrices, sino la matriz que tiene en cada entrada (i, j) el producto de las entradas (i, j) de cada
una de las dos matrices. Esto a veces es útil, pero no es el producto de matrices. De manera
similar, si M es una matriz y entráis M^n, el resultado no es la potencia n-ésima de M , sino la
matriz que tiene en cada entrada la potencia n-ésima de la entrada correspondiente de M . De
nuevo, esto a veces es útil, pero muy pocas veces coincide con la potencia n-ésima de M .
> A = matrix ( c (1 ,2 ,1 ,3) , nrow =2 , byrow = TRUE )
> A
[ ,1] [ ,2]
[1 ,] 1 2
[2 ,] 1 3
> B = matrix ( c ( - 2 ,4 ,3 ,1 ,0 ,2) , nrow =3 , byrow = TRUE )
> B
[ ,1] [ ,2]
[1 ,] -2 4
1
Si necesitáis repasar los conceptos básicos sobre operaciones de matrices que aparecen en esta sec-
ción, podéis consultar la entrada sobre matrices en la Wikipedia, http://es.wikipedia.org/wiki/Matriz_
(matemáticas).
4-8
[2 ,] 3 1
[3 ,] 0 2
> C = matrix ( c (1 ,0 ,1 ,2 ,1 ,0) , nrow =2 , byrow = TRUE )
> C
[ ,1] [ ,2] [ ,3]
[1 ,] 1 0 1
[2 ,] 2 1 0
> t ( B ) # Traspuesta
[ ,1] [ ,2] [ ,3]
[1 ,] -2 3 0
[2 ,] 4 1 2
> t ( B ) + C # Suma
[ ,1] [ ,2] [ ,3]
[1 ,] -1 3 1
[2 ,] 6 2 2
> 5 * A # Producto por escalar
[ ,1] [ ,2]
[1 ,] 5 10
[2 ,] 5 15
> C %* %B # Producto
[ ,1] [ ,2]
[1 ,] -2 6
[2 ,] -1 9
> ( C %* %B ) %* %A # Producto
[ ,1] [ ,2]
[1 ,] 4 14
[2 ,] 8 25
> A ^2 # Esto no es elevar al cuadrado
[ ,1] [ ,2]
[1 ,] 1 4
[2 ,] 1 9
> A %* %A # Esto s í
[ ,1] [ ,2]
[1 ,] 3 8
[2 ,] 4 11
Al multiplicar matrices por vectores, R trata por defecto estos últimos como vectores columna,
pero si en alguna situación concreta la manera natural de entender un vector es como vector
fila, lo hace sin ningún reparo. Veamos algunos ejemplos:
> A = rbind ( c (1 ,2) ,c (3 ,4) )
> A
[ ,1] [ ,2]
[1 ,] 1 2
[2 ,] 3 4
> v = c (5 ,6)
El producto ✓ ◆ ✓ ◆
1 2 5
·
3 4 6
se obtiene mediante
4-9
> A %* %v
[ ,1]
[1 ,] 17
[2 ,] 39
El producto ✓ ◆
1 2
5 6 ·
3 4
se obtiene mediante
> v %* %A
[ ,1] [ ,2]
[1 ,] 23 34
El producto ✓ ◆
5
5 6 ·
6
se obtiene mediante
> v %* %v
[ ,1]
[1 ,] 61
El producto ✓ ◆
5
· 5 6
6
se obtiene mediante
> v %* %t ( v )
[ ,1] [ ,2]
[1 ,] 25 30
[2 ,] 30 36
La versión básica de R no lleva ninguna función para calcular potencias de matrices, y hay
que cargar algún paquete adecuado para disponer de ella.2 Por ejemplo, el paquete Biodem
dispone de la función mtx.exp, y el paquete expm dispone de la operación %^%. No obstante,
hay que tener en cuenta que estas funciones no calculan las potencias de manera exacta, sino
que emplean algoritmos de cálculo numérico para aproximarlas a cambio de calcularlas rápido,
y por lo tanto no siempre dan el resultado exacto.
> A = matrix ( c (1 ,2 ,1 ,3) , nrow =2 , byrow = TRUE )
> # Instalamos y cargamos el paquete " Biodem "
...
> mtx . exp (A , 20) # A ^20
[ ,1] [ ,2]
[1 ,] 58063278153 158631825968
2
Explicamos el manejo de paquetes en la Sección 0.5.
4-10
[2 ,] 79315912984 216695104121
> # Instalamos y cargamos el paquete " expm "
...
> A %^ %20 # A ^20
[ ,1] [ ,2]
[1 ,] 58063278153 158631825968
[2 ,] 79315912984 216695104121
Para ganar en rapidez, R calcula los determinantes usando un método numérico que a veces
produce efectos no deseados como el siguiente:
> A = matrix ( c (3 ,10 ,30 ,100) , nrow =2)
> A
[ ,1] [ ,2]
[1 ,] 3 30
[2 ,] 10 100
> det ( A )
[1] 3.552714 e - 14
Pero, de hecho,
3 30
= 3 · 100 30 · 10 = 0.
10 100
Por lo tanto, este determinante 3.552714 · 10 14
es en realidad 0.
El rango de una matriz A se puede calcular mediante la instrucción qr(A)$rank.
> X = matrix ( c (0 ,1 ,0 , - 7 ,3 , - 1 ,16 , - 3 ,4) , nrow =3 , byrow = TRUE )
> X
[ ,1] [ ,2] [ ,3]
[1 ,] 0 1 0
[2 ,] -7 3 -1
[3 ,] 16 -3 4
> det ( X )
[1] 12
> qr ( X ) $ rank
[1] 3
> Y = rbind ( rep (0 ,3) , rep (1 ,3) )
> Y
[ ,1] [ ,2] [ ,3]
[1 ,] 0 0 0
[2 ,] 1 1 1
4-11
> qr ( Y ) $ rank
[1] 1
Podemos calcular la inversa de una matriz invertible con la instrucción solve. Por ejemplo,
para calcular la inversa A 1 de la matriz
0 1
1 3 4
A=@ 0 2 1 A
2 1 2
podemos entrar lo siguiente:
> A = matrix ( c (1 ,3 ,4 ,0 ,2 , - 1 ,2 ,1 ,2) , nrow =3 , byrow = TRUE )
> solve ( A )
[ ,1] [ ,2] [ ,3]
[1 ,] - 0.2941176 0.1176471 0.64705882
[2 ,] 0.1176471 0.3529412 - 0.05882353
[3 ,] 0.2352941 - 0.2941176 - 0.11764706
Obtenemos 0 1
0.2941176 0.1176471 0.64705882
A 1
= @ 0.1176471 0.3529412 0.05882353 A .
0.2352941 0.2941176 0.11764706
Comprobemos si esta matriz es realmente la inversa de A:
> A %* %solve ( A )
[ ,1] [ ,2] [ ,3]
[1 ,] 1.000000 e +00 0 0.000000 e +00
[2 ,] 5.551115 e - 17 1 - 2.775558 e - 17
[3 ,] 0.000000 e +00 0 1.000000 e +00
> solve ( A ) %* %A
[ ,1] [ ,2] [ ,3]
[1 ,] 1 0.000000 e +00 0.000000 e +00
[2 ,] 0 1.000000 e +00 1.110223 e - 16
[3 ,] 0 2.775558 e - 17 1.000000 e +00
4-12
solve(A, b),
donde A es la matriz A del sistema y b es el vector de términos independientes
b = (b1 , . . . , bn ).
podemos entrar
> A = matrix ( c (1 ,6 , - 3 ,2 , - 1 ,1 ,1 ,1 , - 1) , nrow =3 , byrow = TRUE )
> A
[ ,1] [ ,2] [ ,3]
[1 ,] 1 6 -3
[2 ,] 2 -1 1
[3 ,] 1 1 -1
> b = c (7 ,2 ,3)
> solve (A , b )
[1] 1.6666667 0.4444444 - 0.8888889
3
Si necesitáis repasar las definiciones de vector y valor propio de una matriz y de descomposición canónica
de una matriz diagonalizable, y por qué son importantes, podéis consultar las entradas correspondientes de
la Wikipedia: http://es.wikipedia.org/wiki/Vector_propio_y_valor_propio y http://es.wikipedia.
org/wiki/Matriz_diagonalizable.
4-13
[1 ,] 2 6 -8
[2 ,] 0 6 -3
[3 ,] 0 2 1
> eigen ( A )
$ values
[1] 4 3 2
$ vectors
[ ,1] [ ,2] [ ,3]
[1 ,] 0.2672612 - 0.8164966 1
[2 ,] 0.8017837 0.4082483 0
[3 ,] 0.5345225 0.4082483 0
El objeto values es un vector con los valores propios, y el objeto vectors es una matriz
cuyas columnas son vectores propios: la primera columna es un vector propio del primer valor
propio del vector values, la segunda lo es del segundo, y así sucesivamente. De este modo, del
resultado anterior deducimos que los valores propios de A son 2, 3 y 4 y que
0 1 0 1 0 1
1 0.8164966 0.2672612
@ 0 A , @ 0.4082483 A , @ 0.8017837 A
0 0.4082483 0.5345225
son vectores propios de A de valores propios 2, 3 y 4, respectivamente (o, para ser precisos, los
dos últimos son vectores propios de valor propio 3 y 4 redondeados a 7 cifras decimales).
Es importante tener en cuenta algunas propiedades de la función eigen:
Si hay algún valor propio con multiplicidad mayor que 1, da tantos vectores de este valor
propio como su multiplicidad. Además, en este caso procura que estos vectores propios
sean linealmente independientes. Por lo tanto, cuando da vectores propios repetidos de
algún valor propio es porque para este valor propio no existen tantos vectores propios
linealmente independientes como su multiplicidad y, por consiguiente, la matriz no es
diagonalizable.
4-14
Del resultado de eigen(A) se puede obtener una descomposición canónica
1
A=P ·D·P
de una matriz diagonalizable A: basta tomar como D la matriz diagonal que tiene como diagonal
principal el vector eigen(A)$values y como P la matriz eigen(A)$vectors.
Para construir una matriz diagonal cuya diagonal principal sea un vector dado, podemos usar
la instrucción diag(vector ). Si aplicamos diag a un número n, produce la matriz identidad de
orden n.
> diag ( c (2 ,5 , - 1) )
[ ,1] [ ,2] [ ,3]
[1 ,] 2 0 0
[2 ,] 0 5 0
[3 ,] 0 0 -1
> diag (3)
[ ,1] [ ,2] [ ,3]
[1 ,] 1 0 0
[2 ,] 0 1 0
[3 ,] 0 0 1
La función diag ya había salido en la Sección 4.2: si se aplica a una matriz, se obtiene el
vector formado por sus entradas (1, 1), (2, 2), . . .; ahora vemos que si se aplica a un vector,
produce una matriz diagonal.
> B = matrix (1:10 , nrow =2 , byrow = TRUE )
> B
[ ,1] [ ,2] [ ,3] [ ,4] [ ,5]
[1 ,] 1 2 3 4 5
[2 ,] 6 7 8 9 10
> diag ( B )
[1] 1 7
> diag ( diag ( B ) )
[ ,1] [ ,2]
[1 ,] 1 0
[2 ,] 0 7
Veamos un ejemplo de uso de eigen para calcular una descomposición canónica. Como hemos
visto, la matriz 0 1
2 6 8
A=@ 0 6 3 A
0 2 1
es de orden 3 y tiene sus tres valores propios diferentes. Por lo tanto, es diagonalizable y las
matrices de una descomposición canónica serían las siguientes:
> D = diag ( eigen ( A ) $ values ) # Matriz diagonal D de valores propios
> D
[ ,1] [ ,2] [ ,3]
[1 ,] 4 0 0
[2 ,] 0 3 0
[3 ,] 0 0 2
4-15
> P = eigen ( A ) $ vectors # Matriz P de vectores propios
> P
[ ,1] [ ,2] [ ,3]
[1 ,] 0.2672612 - 0.8164966 1
[2 ,] 0.8017837 0.4082483 0
[3 ,] 0.5345225 0.4082483 0
> P %* %D %* %solve ( P )
[ ,1] [ ,2] [ ,3]
[1 ,] 2 6 -8
[2 ,] 0 6 -3
[3 ,] 0 2 1
> A
[ ,1] [ ,2] [ ,3]
[1 ,] 2 6 -8
[2 ,] 0 6 -3
[3 ,] 0 2 1
$ vectors
[ ,1] [ ,2] [ ,3]
[1 ,] - 0.1301889 - 0.1525742 - 0.1525742
[2 ,] - 0.3905667 - 0.3651484 - 0.3651484
[3 ,] 0.9113224 0.9128709 0.9128709
Da dos veces el mismo vector propio de valor propio 2. Esto significa que B no tiene dos vectores
propios linealmente independientes de este valor propio, y, por lo tanto, no es diagonalizable.
4-16
para elevar al cuadrado la matriz
✓ ◆
3 2i 5 + 3
,
1 + 2i 2 i
podemos entrar:
> A = matrix ( c (3 - 2i ,5+3 i ,1+2 i ,2 - 1 i ) , nrow =2 , byrow = TRUE )
> A
[ ,1] [ ,2]
[1 ,] 3 - 2 i 5+3 i
[2 ,] 1+2 i 2 - 1 i
> A %* %A
[ ,1] [ ,2]
[1 ,] 4+1 i 34+0 i
[2 ,] 11+7 i 2+9 i
$ vectors
[ ,1] [ ,2]
[1 ,] 0.8483705+0.000000 i 0.8519823+0.000000 i
[2 ,] 0.4695014+0.244614 i - 0.5216168 - 0.045189 i
Pero resulta que el determinante de una matriz es igual al producto de sus valores propios,
incluyendo repeticiones. Por lo tanto, para calcular el determinante de una matriz compleja A
podemos usar prod(eigen(A)$values).
> A = matrix ( c (3 - 2i , 5+3 i , 1+2 i , 2 - 1 i ) , nrow =2 , byrow = TRUE )
> prod ( eigen ( A ) $ values )
[1] 5 - 20 i
4-17
4.7. Guía rápida de funciones
matrix sirve para construir una matriz a partir de un vector. Algunos parámetros impor-
tantes:
• byrow: un parámetro lógico para indicar si la matriz se construye por filas (igualado
a TRUE) o por columnas (valor por defecto).
• nrow: el número de filas.
• ncol: el número de columnas.
dimnames permite poner nombres a las filas y las columnas de una matriz.
matriz [...,...] se usa para especificar un elemento, una fila, una columna o una sub-
matriz de la matriz. Si extraemos una fila o una columna con el parámetro drop=FALSE,
el resultado es una matriz y no un vector.
diag tiene dos significados: aplicada a un vector, construye una matriz diagonal, y aplicada
a una matriz, extrae su diagonal principal.
colSums y rowSums calculan, respectivamente, las sumas de las entradas de cada una de
las columnas y de cada una de las filas de una matriz.
colMeans y rowMeans calculan, respectivamente, las medias de cada una de las columnas
y de cada una de las filas de una matriz.
4-18
solve, aplicada a una matriz invertible A, calcula su inversa A 1 , y aplicada a una matriz
invertible A y un vector b, calcula A 1 · b.
eigen calcula los valores y vectores propios de una matriz. El resultado es una list con
dos componentes:
sea mínimo. De este modo encontraríamos la recta de regresión por mínimos cuadrados. Resulta
(no lo demostraremos aquí) que los coeficientes a y b de esta recta de regresión se obtienen por
medio de la fórmula ✓ ◆
b
= (D2 · D2t ) 1 · D2 · w,
a
donde 0 1
y1 ✓ ◆
B .. C 1 1 ... 1
w = @ . A, D2 = .
x 1 x 2 . . . xk
yk
(a) Calculad de este modo los valores de a y b cuando las observaciones son las de la Tabla
4.1 (es la Tabla 2.1 de la Lección 2), y comprobad que obtenéis el mismo resultado que
obteníamos en su momento con la función lm.
x: (edad, en años) 1 2 3 5 7 9 11 13
y: (altura, en cm) 76.11 86.45 95.27 109.18 122.03 133.73 143.73 156.41
De manera similar, si queremos obtener una función cuadrática y = ax2 +bx+c que aproxime
los pares (xi , yi )y=1,...,k , podemos buscar los coeficientes a, b, c que minimicen el valor de
k
X
(ax2i + bxi + c yi ) 2 .
i=1
4-19
Estos coeficientes se obtienen de manera similar, por medio de la fórmula
0 1
c
@ b A = (D3 · D3t ) 1 · D3 · w,
a
(b) Calculad los valores de a, b, c para los pares (x, y) de la Tabla 4.1.
(c) Con R, podemos calcular estos coeficientes de la manera siguiente: si definimos un nuevo
vector z con los cuadrados de los valores de x, y aplicamos la función
lm(y˜x+z)
4-20
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 3
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 5
Data frames
Habitualmente, dispondremos de una serie de datos que describirán algunos aspectos de un
conjunto de individuos, y querremos analizarlos. El análisis estadístico de estos datos puede ser
de dos tipos básicos:
En las próximas lecciones explicaremos algunas técnicas básicas de estadística descriptiva orien-
tadas al análisis de datos. Estas técnicas consistirán en una serie de medidas, gráficos y modelos
descriptivos que nos permitirán resumir y explorar un conjunto de datos, con el objetivo final
de entenderlos lo mejor posible. De todas formas, ambos tipos de análisis estadístico están
relacionados. Así, por un lado, cualquier análisis inferencial se suele empezar explorando los
datos que se usarán, y por otro, muchas técnicas descriptivas permiten estimar propiedades de
la población de la que se ha extraído la muestra. Por citar un ejemplo, la media aritmética de
las alturas de una muestra de individuos nos da un valor representativo de esta muestra, pero
también estima la media de las alturas del total de la población, si la muestra es aleatoria.
Los datos de los que disponemos para su análisis suelen ser multidimensionales, en el sentido
de que observamos varias características de una serie de individuos. Estos datos se tienen que
registrar de alguna manera. Normalmente, los guardaremos en un archivo de ordenador con
un formato preestablecido. Los formatos de almacenamiento de datos en un ordenador son
diversos: texto simple (codificado en diferentes formatos: ASCII, isolatin, utf8. . . ), hojas de
cálculo (archivos de Open Office o Excel), bases de datos, etc. Una de las maneras básicas de
almacenar datos es en forma de tablas de datos, pequeñas bases de datos donde se han anotado
los valores de algunas variables para una serie de observaciones.
Como ya comentamos en la Lección 2, la manera más conveniente de guardar en R una tabla
de datos es en forma de data frame. En concreto, un data frame es una tabla de doble entrada,
formada por variables en las columnas y observaciones de estas variables en las filas, de manera
que cada fila contiene los valores de las variables para un mismo caso o individuo. En este
sentido, un data frame tiene la apariencia de una matriz, pero con la diferencia de que cada
columna de un data frame puede contener datos de un tipo diferente, siempre que todos los
datos de una misma columna sean del mismo tipo, porque corresponden a observaciones de
una misma propiedad: así, una columna puede estar formada por números, por ejemplo, alguna
medida; otra, por palabras, por ejemplo, la especie del individuo; otra, por valores lógicos, por
ejemplo, que describan si una cierta propiedad está presente o ausente en el individuo; etc. De
esta manera, las columnas de un data frame son vectores o factores, mientras que las filas son
lists.
Los tipos de datos que consideramos en este curso son los siguientes:
Datos de tipo atributo, o cualitativos. Son los que expresan una cualidad del individuo,
tales como el sexo, el DNI, la especie. . . En R, guardaremos las listas de datos cualitativos
en vectores (habitualmente, de palabras), o en factores si vamos a usarlos para clasificar
individuos.
Datos ordinales. Son datos similares a los cualitativos, con la única diferencia de que
se pueden ordenar de manera natural. Por ejemplo, los niveles de calidad ambiental de
un ecosistema (malo, regular, normal, bueno, muy bueno) o las calificaciones en un exa-
men (suspenso, aprobado, notable, sobresaliente) son datos ordinales. En cambio, no se
pueden ordenar de manera significativa los sexos o las especies de los individuos. En R,
guardaremos las listas de datos ordinales en factores ordenados.
Datos cuantitativos. Son datos que se refieren a medidas, tales como edades, longitu-
des, pesos, tiempos, números de individuos, etc. En R, guardaremos las listas de datos
cuantitativos en vectores de números.
5-2
formas, si trabajamos directamente sobre iris y lo echamos a perder, lo podemos recuperar
ejecutando la instrucción data(iris), que devolverá a este data frame su contenido original.
> d . f = iris
obtendríamos el contenido del data frame en la consola: una larga lista de datos formada por las
150 filas de la tabla; naturalmente, no vamos a copiar aquí esta salida. Para echarle un vistazo
al data frame, y así poder entender su estructura y conocer los nombres de sus variables, os
recomendamos usar la instrucción
> View ( d . f )
También podemos consultar en la consola las primeras filas del data frame, aplicando la
función head al data frame y al número de filas que queremos que muestre; su valor por defecto
es 6.
> head ( d .f ,5) # Las primeras 5 filas
Sepal . Length Sepal . Width Petal . Length Petal . Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
La función tail, con una estructura similar a head, nos muestra la «cola» de la tabla.
5-3
> tail ( d .f ,5) # Las ú ltimas 5 filas
Sepal . Length Sepal . Width Petal . Length Petal . Width Species
146 6.7 3.0 5.2 2.3 virginica
147 6.3 2.5 5.0 1.9 virginica
148 6.5 3.0 5.2 2.0 virginica
149 6.2 3.4 5.4 2.3 virginica
150 5.9 3.0 5.1 1.8 virginica
Observad que las columnas del data frame tienen nombres (R los llama names) y las filas, que
corresponden a individuos, tienen como identificador (R llama a estos identificadores rownames)
un número natural correlativo que va del 1 al 150. Podemos comprobar también que, en cada
fila, la columna Species contiene una palabra que describe la especie de la flor, mientras que
las otras cuatro columnas contienen números que corresponden a medidas. Como veremos, la
columna Species no es un vector, sino un factor (con niveles las tres especies de iris: setosa,
versicolor y virginica), lo que nos facilita el uso de esta variable para clasificar las flores.
Si queremos conocer la estructura global de un data frame, podemos usar la función str.
> str ( d . f )
’ data . frame ’: 150 obs . of 5 variables :
$ Sepal . Length : num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal . Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal . Length : num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal . Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w / 3 levels " setosa " ," versicolor " ,..: 1 1 1
1 1 1 1 1 1 ...
El resultado de esta instrucción nos muestra la estructura del objeto d.f. Nos dice que es un
data frame formado por 150 observaciones (filas) de 5 variables (columnas), y de cada variable
nos da su nombre (precedido de un signo $) y su tipo de datos: las cuatro primeras son variables
cuantitativas, formadas por vectores numéricos (num), y la quinta es una variable cualitativa,
un factor con (w, de with) 3 niveles que corresponden a las especies. Además, nos muestra los
primeros valores de cada variable: en el caso del factor, los unos significan «setosa», su primer
nivel.
Las funciones siguientes nos permiten obtener los nombres de las variables, los identificadores
de las filas y las dimensiones de un data frame:
dimnames: Produce una list formada por dos vectores: el de los identificadores de las
filas y el de los nombres de las columnas.
5-4
> rownames ( d . f )
[1] " 1 " "2" "3" "4" "5" "6" "7" "8" "9" " 10 "
[11] " 11 " " 12 " " 13 " " 14 " " 15 " " 16 " " 17 " " 18 " " 19 " " 20 "
[21] " 21 " " 22 " " 23 " " 24 " " 25 " " 26 " " 27 " " 28 " " 29 " " 30 "
[31] " 31 " " 32 " " 33 " " 34 " " 35 " " 36 " " 37 " " 38 " " 39 " " 40 "
[41] " 41 " " 42 " " 43 " " 44 " " 45 " " 46 " " 47 " " 48 " " 49 " " 50 "
[51] " 51 " " 52 " " 53 " " 54 " " 55 " " 56 " " 57 " " 58 " " 59 " " 60 "
[61] " 61 " " 62 " " 63 " " 64 " " 65 " " 66 " " 67 " " 68 " " 69 " " 70 "
[71] " 71 " " 72 " " 73 " " 74 " " 75 " " 76 " " 77 " " 78 " " 79 " " 80 "
[81] " 81 " " 82 " " 83 " " 84 " " 85 " " 86 " " 87 " " 88 " " 89 " " 90 "
[91] " 91 " " 92 " " 93 " " 94 " " 95 " " 96 " " 97 " " 98 " " 99 " " 100 "
[101] " 101 " " 102 " " 103 " " 104 " " 105 " " 106 " " 107 " " 108 " " 109 " " 110 "
[111] " 111 " " 112 " " 113 " " 114 " " 115 " " 116 " " 117 " " 118 " " 119 " " 120 "
[121] " 121 " " 122 " " 123 " " 124 " " 125 " " 126 " " 127 " " 128 " " 129 " " 130 "
[131] " 131 " " 132 " " 133 " " 134 " " 135 " " 136 " " 137 " " 138 " " 139 " " 140 "
[141] " 141 " " 142 " " 143 " " 144 " " 145 " " 146 " " 147 " " 148 " " 149 " " 150 "
> dimnames ( d . f )
[[1]]
[1] " 1 " "2" "3" "4" "5" "6" "7" "8" "9" " 10 "
[11] " 11 " " 12 " " 13 " " 14 " " 15 " " 16 " " 17 " " 18 " " 19 " " 20 "
[21] " 21 " " 22 " " 23 " " 24 " " 25 " " 26 " " 27 " " 28 " " 29 " " 30 "
[31] " 31 " " 32 " " 33 " " 34 " " 35 " " 36 " " 37 " " 38 " " 39 " " 40 "
[41] " 41 " " 42 " " 43 " " 44 " " 45 " " 46 " " 47 " " 48 " " 49 " " 50 "
[51] " 51 " " 52 " " 53 " " 54 " " 55 " " 56 " " 57 " " 58 " " 59 " " 60 "
[61] " 61 " " 62 " " 63 " " 64 " " 65 " " 66 " " 67 " " 68 " " 69 " " 70 "
[71] " 71 " " 72 " " 73 " " 74 " " 75 " " 76 " " 77 " " 78 " " 79 " " 80 "
[81] " 81 " " 82 " " 83 " " 84 " " 85 " " 86 " " 87 " " 88 " " 89 " " 90 "
[91] " 91 " " 92 " " 93 " " 94 " " 95 " " 96 " " 97 " " 98 " " 99 " " 100 "
[101] " 101 " " 102 " " 103 " " 104 " " 105 " " 106 " " 107 " " 108 " " 109 " " 110 "
[111] " 111 " " 112 " " 113 " " 114 " " 115 " " 116 " " 117 " " 118 " " 119 " " 120 "
[121] " 121 " " 122 " " 123 " " 124 " " 125 " " 126 " " 127 " " 128 " " 129 " " 130 "
[131] " 131 " " 132 " " 133 " " 134 " " 135 " " 136 " " 137 " " 138 " " 139 " " 140 "
[141] " 141 " " 142 " " 143 " " 144 " " 145 " " 146 " " 147 " " 148 " " 149 " " 150 "
[[2]]
[1] " Sepal . Length " " Sepal . Width " " Petal . Length " " Petal . Width "
[5] " Species "
> dim ( d . f )
[1] 150 5
Fijaos en que el resultado de rownames son los identificadores de las filas entre comillas, es
decir, considerados como palabras; R entiende siempre que estos identificadores son palabras,
aunque, como en este caso, sean números.
Recordaréis que se puede obtener el valor de una componente de una list añadiendo al
nombre de esta última un sufijo formado por el signo $ seguido del nombre de la componente.
De manera similar, para obtener una columna concreta de un data frame basta añadir a su
nombre un sufijo formado por el signo $ seguido del nombre de la variable; el resultado será un
vector o un factor, según cómo esté definida la columna dentro del data frame.
> d . f $ Sepal . Length [1:30]
5-5
[1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8
[16] 5.7 5.4 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7
> d . f $ Species [1:30]
[1] setosa setosa setosa setosa setosa setosa setosa setosa setosa
[10] setosa setosa setosa setosa setosa setosa setosa setosa setosa
[19] setosa setosa setosa setosa setosa setosa setosa setosa setosa
[28] setosa setosa setosa
Levels : setosa versicolor virginica
Como también pasaba con las componentes de las list, las variables de un data frame son
internas, no están definidas en el entorno global de trabajo de R.
> Sepal . Length
Error : object ’ Sepal . Length ’ not found
En la Sección 5.8 explicaremos cómo podemos declarar las variables internas de un data frame
como variables globales, y así poder usarlas directamente por su nombre, sin tener que añadirles
delante el nombre del data frame y el $.
Los data frames comparten con las matrices el uso de los corchetes para extraer trozos por
filas y columnas. Los resultados que se obtienen son de nuevo data frames, y tanto los nombres
de las columnas como los identificadores de las filas se heredan del data frame original; podemos
comprobarlo en los siguientes ejemplos:
> d . f [1:5 , ] # La subtabla de las 5 primeras filas
Sepal . Length Sepal . Width Petal . Length Petal . Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
> d . f [1:5 , 1:3] # La subtabla de las 5 primeras filas y las 3
primeras columnas
Sepal . Length Sepal . Width Petal . Length
1 5.1 3.5 1.4
2 4.9 3.0 1.4
3 4.7 3.2 1.3
4 4.6 3.1 1.5
5 5.0 3.6 1.4
> d . f [ d . f $ Species == " virginica " & d . f $ Sepal . Length >7 , ] # La
subtabla de filas con Species = virginica y Sepal . Length >7
Sepal . Length Sepal . Width Petal . Length Petal . Width Species
103 7.1 3.0 5.9 2.1 virginica
106 7.6 3.0 6.6 2.1 virginica
108 7.3 2.9 6.3 1.8 virginica
110 7.2 3.6 6.1 2.5 virginica
118 7.7 3.8 6.7 2.2 virginica
119 7.7 2.6 6.9 2.3 virginica
123 7.7 2.8 6.7 2.0 virginica
126 7.2 3.2 6.0 1.8 virginica
130 7.2 3.0 5.8 1.6 virginica
131 7.4 2.8 6.1 1.9 virginica
5-6
132 7.9 3.8 6.4 2.0 virginica
136 7.7 3.0 6.1 2.3 virginica
> d . f [ d . f $ Species == " virginica " & d . f $ Sepal . Length >7 , ][1:4 ,] # La
subtabla de las 4 primeras filas de la anterior
Sepal . Length Sepal . Width Petal . Length Petal . Width Species
103 7.1 3.0 5.9 2.1 virginica
106 7.6 3.0 6.6 2.1 virginica
108 7.3 2.9 6.3 1.8 virginica
110 7.2 3.6 6.1 2.5 virginica
En todos los casos, obtenemos subtablas del data frame d.f con sus filas y columnas determi-
nadas por las expresiones entre corchetes.
sep. Como ya ocurría con la función scan, la función read.table entiende por defecto
que las separaciones entre columnas en el fichero están marcadas por espacios en blanco.
Si esta separación está marcada por comas, signos de punto y coma, tabuladores o de
cualquier otra manera, hay que especificarlo con este parámetro.
Por ejemplo, tenemos que especificar sep="," para indicar que las separaciones entre
columnas son comas, sep=";" para indicar que las separaciones entre columnas son sig-
nos de punto y coma, y sep="\t" para indicar que las separaciones entre columnas son
tabuladores.
header. Si la tabla que importamos tiene una primera fila con los nombres de las co-
lumnas, es conveniente especificarlo con el parámetro header=TRUE; en caso contrario, y
según cómo esté formateado el fichero, puede que R entienda la fila de los nombres de
las variables como la primera fila de observaciones. Si, en cambio, las columnas de la
tabla que importamos no tienen nombre, no hace falta especificar el parámetro header
(o usar header=FALSE, que es su valor por defecto), y después ya pondremos nombres a
las variables con la función names (véase la Sección 5.4).
dec. Como ya explicamos en la función scan, podemos usar el parámetro dec para espe-
cificar el separador decimal, si no es un punto.
5-7
stringsAsFactors. Por defecto, read.table transforma en factores las columnas de
palabras de la tabla que importa. Para impedir esta transformación, de manera que los
vectores de palabras se importen como tales, podemos usar stringsAsFactors=FALSE.
encoding. Como también ya explicamos en la función scan (página 3-4), este parámetro
sirve para indicar la codificación de alfabeto del fichero que se va a importar, y se ha
de usar si dicho fichero contiene palabras con letras acentuadas o caracteres especiales y
su codificación no coincide con la que espera nuestro ordenador: lo notaremos porque si
importamos el fichero sin especificar este parámetro, los acentos se importan mal. Como
los ficheros externos que usamos en este curso están codificados en utf8, los usuarios de
Windows de vez en cuando tendrán que usar encoding="latin1". Avisaremos cada vez
que sea necesario.
http://aprender.uib.es/Rdir/NotaHermanos.txt
http://aprender.uib.es/Rdir/NotaHermanosc.txt
Ambos ficheros son básicamente la misma tabla de datos, que recoge, para algunos estudiantes
de primer curso de la UIB de hace unos años, el grado en el que estaban matriculados (Bio-
logía, «BL», o Bioquímica, «BQ»), su número de hermanos y la nota que obtuvieron en un
determinado examen; la diferencia es que, en el primero, las columnas están separadas por es-
pacios en blanco, y en el segundo, por comas. Ambos ficheros contienen una primera fila con los
nombres de las variables. Están codificados en utf8, pero como no contienen letras acentuadas
ni caracteres especiales, no tenéis que preocuparos por su codificación.
Para crear un data frame llamado NH1 a partir de la copia local del fichero NotaHermanos.txt,
basta aplicar la función read.table a su nombre entre comillas y especificar header=TRUE.
> NH1 = read . table ( " NotaHermanos . txt " , header = TRUE )
Es una buena costumbre, una vez definido un data frame a partir de un fichero externo,
comprobar que se ha importado bien. Como una tabla de datos puede tener muchas filas, verla
entera en la consola puede ser poco práctico; lo mejor es usar las funciones str y View o head.
Os aconsejamos que, siempre que vayáis a trabajar con un data frame, le echéis antes un vistazo
con estas funciones para comprobar su estructura, los nombres de sus variables, los tipos de
sus datos, etc. Esto es especialmente importante cuando se trata de data frames importados,
porque si no hemos especificado los parámetros adecuados, puede que en el proceso de lectura
y definición del data frame se hayan perdido los nombres de las variables o la estructura de
columnas.
> head ( NH1 )
Grado Hermanos Nota
1 BQ 1 9.3
2 BL 1 3.5
3 BL 1 5.5
4 BL 1 7.7
5 BL 2 8.1
5-8
6 BQ 0 8.6
> str ( NH1 )
’ data . frame ’: 87 obs . of 3 variables :
$ Grado : Factor w / 2 levels " BL " ," BQ " : 2 1 1 1 1 2 2 1 1 1 ...
$ Hermanos : int 1 1 1 1 2 0 1 1 1 1 ...
$ Nota : num 9.3 3.5 5.5 7.7 8.1 8.6 9.7 4 1.6 3.1 ...
Sin el parámetro header=TRUE, R ha entendido que Grado, Hermanos y Nota son elementos
de sus columnas respectivas, y no sus nombres; en consecuencia, por un lado, ha llamado
por defecto V1, V2 y V3 a las columnas del data frame, y por otro, como Grado, Hermanos y
Nota son palabras y los datos de cada variable de un data frame han de ser del mismo tipo,
ha considerado que todas las entradas de cada columna eran palabras. Finalmente, como, al
crear un data frame, R interpreta los vectores de palabras como factores si no especificamos
lo contrario, hemos obtenido un data frame formado por tres factores, y hemos perdido la
información numérica que contenían las columnas con el número de hermanos y la nota.
Para importar el fichero NotaHermanosc.txt, donde las columnas están separadas por comas,
hay que usar el parámetro sep=",".
> NH2 = read . table ( " NotaHermanosc . txt " , header = TRUE , sep = " ," )
> str ( NH2 )
’ data . frame ’: 87 obs . of 3 variables :
$ Grado : Factor w / 2 levels " BL " ," BQ " : 2 1 1 1 1 2 2 1 1 1 ...
$ Hermanos : int 1 1 1 1 2 0 1 1 1 1 ...
$ Nota : num 9.3 3.5 5.5 7.7 8.1 8.6 9.7 4 1.6 3.1 ...
Sin este parámetro, R hubiera entendido cada fila como una sola palabra.
> NH2 . mal = read . table ( " NotaHermanosc . txt " , header = TRUE )
> str ( NH2 . mal )
’ data . frame ’: 87 obs . of 1 variable :
$ Grado . Hermanos . Nota : Factor w / 71 levels " BL ,0 ,2.0 " ," BL ,0 ,4.0 "
,..: 62 12 18 24 38 47 63 15 8 11 ...
5-9
’ data . frame ’: 87 obs . of 3 variables :
$ Grado : chr " BQ " " BL " " BL " " BL " ...
$ Hermanos : int 1 1 1 1 2 0 1 1 1 1 ...
$ Nota : num 9.3 3.5 5.5 7.7 8.1 8.6 9.7 4 1.6 3.1 ...
Naturalmente, para poder importar un fichero de Internet, hay que estar conectados a Internet,
el servidor que aloja el fichero tiene que funcionar, y además no tiene que requerir una palabra
clave para acceder.
5-10
Podemos indicar si tiene una primera fila con los nombres de las variables (en el campo
Heading).
Podemos especificar el signo que separa las columnas (en el campo Separator ) y el signo
que separa la parte entera de la decimal (en el campo Decimal ).
La ventaja de este menú es que en su campo superior derecho vemos el fichero original, lo
que nos ayuda a rellenar los campos anteriores, y en el inferior derecho vemos el aspecto del
data frame que creamos con nuestras elecciones. Su inconveniente es que, si queremos que
nuestro análisis de datos sea reproducible por otras personas, es necesario incluir en el guión
que publiquemos la función read.table con los parámetros exactos que hemos usado.
Además de read.table, que sólo sirve para importar tablas en formato texto simple, R dispone
de otras instrucciones similares para importar otros tipos de ficheros. Las más útiles son:
read.xls y read.xlsx, del paquete xlsx, para importar hojas de cálculo tipo Excel u
OpenOffice en formato XLS o XLSX, respectivamente. Estas funciones usan Java; si no
lo tenéis instalado y no lo queréis instalar, lo mejor es que guardéis la hoja de cálculo en
formato CSV (tanto Excel como OpenOffice o Numbers ofrecen esta posibilidad) y uséis
read.csv.
read.mtb y read.spss, del paquete foreign, para importar tablas de datos de Minitab
y SPSS, respectivamente.
Si necesitáis otras funciones de este tipo, entrad help.search("read"), buscad la función que
os convenga y consultad su Ayuda.
Podemos exportar un data frame a un fichero usando la función write.table. Su sintaxis
básica es
write.table(data frame, file="nombre del fichero").
Esta función crea un fichero, en el directorio de trabajo de R, que contiene el data frame que
hemos especificado en el argumento, y lo llama el nombre que hemos especificado en file.
Además, podemos usar el parámetro sep para indicar el signo de separación de columnas en el
fichero que creemos y el parámetro dec para indicar el separador decimal; por ejemplo,
> A = iris [1:5 , 1:4]
> A
Sepal . Length Sepal . Width Petal . Length Petal . Width
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
3 4.7 3.2 1.3 0.2
4 4.6 3.1 1.5 0.2
5 5.0 3.6 1.4 0.2
> write . table (A , file = " trozoiris . txt " )
5-11
guarda el data frame A en un fichero llamado trozoiris.txt. Ahora podemos volver a importar
este fichero.
> B = read . table ( " trozoiris . txt " , header = TRUE ) # Importamos la tabla
> B
Sepal . Length Sepal . Width Petal . Length Petal . Width
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
3 4.7 3.2 1.3 0.2
4 4.6 3.1 1.5 0.2
5 5.0 3.6 1.4 0.2
5-12
Puesto que iremos modificando este data frame, vamos a guardar una copia de seguridad (un
backup) en d.f1bk para poder recuperar su estado original si lo necesitamos.
> d . f1bk = d . f1
Es muy prudente y recomendable que guardéis siempre copias de seguridad de los data frames
que generéis si vais a manipularlos, porque hay cambios en un data frame que son irreversibles
y, por lo tanto, si os arrepentís de haber hecho algún cambio, bien podría ser que no lo pudierais
deshacer.
Vamos a consultar algunas características del data frame d.f1.
> str ( d . f1 )
’ data . frame ’: 7 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2
$ Edad : num 17 18 20 18 18 18 19
$ Hermanos : num 2 0 0 1 1 1 0
> rownames ( d . f1 )
[1] " 1 " " 2 " " 3 " " 4 " " 5 " " 6 " " 7 "
Vemos que la primera variable es un factor con dos niveles, y las otras dos son vectores numéricos
(num). Vemos también que R ha asignado como identificadores de las filas los números del 1 al
7, pero los considera palabras.
La función data.frame dispone de algunos parámetros que permiten ajustar a nuestras ne-
cesidades el data frame que creamos. Los más útiles son los siguientes:
5-13
[1] " Hombre " " Hombre " " Mujer " " Hombre " " Hombre " " Hombre " " Mujer "
Otra manera de crear un data frame con R es usando el editor de datos del que ya hemos
hablado en la Lección 3.3 Recordaréis que, para abrir un objeto de datos con este editor, se le
aplica la función fix. R abre entonces el objeto en una nueva ventana de edición. Los cambios
que realicemos en un objeto con el editor de datos se guardarán cuando cerremos esta ventana.
Para crear un data frame con el editor de datos, lo primero que hay que hacer es crear un
data frame con la primera fila, y luego abrirlo con el editor para ir añadiendo filas (y columnas,
si se desea). La apariencia exacta de esta ventana y la manera de editar el data frame dependen
de la interfaz de R que se use; por ejemplo, en el RStudio de Mac OS X, entraríamos
> d . f3 = data . frame ( Sexo = c ( " Hombre " ) , Edad = c (17) , Hermanos = c (2) )
> fix ( d . f3 )
3
El uso del editor de RStudio para manipular data frames posiblemente requiera la instalación de algún
programa auxiliar; por ejemplo, en el caso del Mac OS X, se tiene que tener instalada la última versión de
XQuartz, que será el programa en el que se abrirá el editor. El instalador de XQuartz se puede descargar de
http://xquartz.macosforge.org.
5-14
5.4. Cómo modificar un data frame
En esta sección veremos la manera de modificar un data frame una vez creado o importado.
Para cambiar los nombres de las variables, podemos usar la instrucción
names(data frame)=vector con los nombres de las variables.
Volvamos al d.f1. Tras recordar su estructura, cambiaremos los nombres de sus variables,
sustituyéndolos por sus iniciales, y volveremos a consultar su estructura para ver cómo han
cambiado estos nombres.
> str ( d . f1 )
’ data . frame ’: 7 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2
$ Edad : num 17 18 20 18 18 18 19
$ Hermanos : num 2 0 0 1 1 1 0
> names ( d . f1 ) = c ( " S " ," E " ," H " )
> str ( d . f1 )
’ data . frame ’: 7 obs . of 3 variables :
$ S : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2
$ E : num 17 18 20 18 18 18 19
$ H : num 2 0 0 1 1 1 0
Si sólo queremos cambiar el nombre de algunas variables, basta redefinir el trozo correspon-
diente del vector names. Así, si en la nueva copia de d.f1 queremos volver a cambiar el nombre
de la variable E por Edad, podríamos usar una de las dos instrucciones siguientes:
names(d.f1)[2]="Edad" o names(d.f1)[names(d.f1)=="E"]="Edad".
Con la primera, cambiaríamos el nombre de la segunda variable por Edad; con la segunda,
cambiaríamos el nombre de la variable llamada E por Edad.
Para modificar los identificadores de las filas, podemos usar la instrucción
rownames(data frame)=vector con los nombres de las filas.
Como cada identificador ha de determinar el individuo al que corresponde la fila, conviene que
estos identificadores sean todos diferentes.
> rownames ( d . f1 ) = paste ( " Alumno " , 1:7 , sep = " " )
> d . f1
S E H
Alumno 1 Hombre 17 2
Alumno 2 Hombre 18 0
Alumno 3 Mujer 20 0
Alumno 4 Hombre 18 1
Alumno 5 Hombre 18 1
Alumno 6 Hombre 18 1
Alumno 7 Mujer 19 0
Como podéis deducir de su efecto, la función paste pega vectores, entrada a entrada, usando
como separador el valor del parámetro sep; el separador por defecto es un espacio en blanco,
por lo que no hubiera hecho falta especificarlo, lo hemos hecho sólo para mostrar el parámetro.
Si en lugar de pegar un vector pegamos un elemento, éste se entiende como un vector constante
5-15
formado por el número adecuado de copias.
Podemos modificar los nombres de las filas y de las columnas simultáneamente con la ins-
trucción
dimnames(data frame)=list(vector con los nombres de las filas, vector con los
nombres de las columnas)
Por ejemplo, vamos a cambiar de golpe en d.f1 los identificadores de las filas, para que ahora
sean números romanos, y los nombres de las variables, para que pasen a ser V1,V2,V3:
> dimnames ( d . f1 ) = list ( c ( " I " ," II " ," III " ," IV " ," V " ," VI " ," VII " ) ,
c ( " V1 " ," V2 " ," V3 " ) )
> d . f1
V1 V2 V3
I Hombre 17 2
II Hombre 18 0
III Mujer 20 0
IV Hombre 18 1
V Hombre 18 1
VI Hombre 18 1
VII Mujer 19 0
Para modificar entradas concretas de un data frame, podemos usar el editor de datos que se
abre con fix, o podemos usar instrucciones similares a las que usábamos en los vectores y las
matrices para modificar entradas.
> d . f1 = d . f1bk # Volvemos a la copia guardada
> d . f1 [1 ,2]= " Joven " # Sustituimos la entrada (1 ,2) por la palabra
" Joven "
> str ( d . f1 )
’ data . frame ’: 7 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2
$ Edad : chr " Joven " " 18 " " 20 " " 18 " ...
$ Hermanos : num 2 0 0 1 1 1 0
¡Vaya! Al introducir un dato de tipo palabra en la variable Edad, toda la columna ha pasado
a ser un vector de palabras: recordemos que R considera del mismo tipo de datos todas las
entradas de una columna de un data frame; y ahora ya no lo podemos arreglar volviendo a
poner un número:
> d . f1 [1 ,2]=17
> str ( d . f1 )
’ data . frame ’: 7 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2
$ Edad : chr " 17 " " 18 " " 20 " " 18 " ...
$ Hermanos : num 2 0 0 1 1 1 0
Parece que hemos estropeado definitivamente el data frame d.f1. Pero no es así: simplemente
tenemos que volver a redefinir la variable Edad como un vector numérico.
Para modificar una columna entera, hay que igualar su nombre (que, recordemos, se especifica
añadiendo al nombre del data frame el sufijo formado por el signo $ seguido del nombre de la
5-16
variable dentro del data frame) a su nuevo valor:
data frame$variable=nuevo valor.
Este nuevo valor puede ser el resultado de un cambio de tipo de datos aplicado a la varia-
ble. R dispone de una serie de funciones de la forma as.tipo_de_objeto, que convierten el
objeto al tipo que expresa el nombre de la función. Ya hemos visto en la Sección 3.4 la fun-
ción as.factor, que transforma un vector en un factor. Otras funciones de este estilo son
as.character, as.integer o as.numeric, que transforman todos los datos de un objeto en
palabras, números enteros o números reales, respectivamente.
Así, para transformar la variable Edad de la última versión estropeada de d.f1 en un vector
numérico, basta entrar lo siguiente:
> d . f1 $ Edad = as . numeric ( d . f1 $ Edad )
> str ( d . f1 )
’ data . frame ’: 7 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2
$ Edad : num 17 18 20 18 18 18 19
$ Hermanos : num 2 0 0 1 1 1 0
Naturalmente, podemos cambiar el contenido de toda una variable de otras maneras que no
sea sólo modificar su tipo de datos: basta igualarla a su nuevo valor.
> d . f1 $ Edad = " Joven "
> d . f1
Sexo Edad Hermanos
1 Hombre Joven 2
5-17
2 Hombre Joven 0
3 Mujer Joven 0
4 Hombre Joven 1
5 Hombre Joven 1
6 Hombre Joven 1
7 Mujer Joven 0
Las filas que añadimos de esta manera son vectores, y por lo tanto sus entradas han de
ser todas del mismo tipo. Por consiguiente, esta opción sólo se puede usar en data frames
cuyas variables contengan todas el mismo tipo de datos.
Si no añadimos las filas inmediatamente siguientes a la última del data frame, los valores
entre su última fila y las que añadimos quedarán no definidos, y aparecerán como NA; esto
puede ser un problema a la hora de aplicar funciones al data frame y además estropea los
factores.
La mejor manera de añadir filas a un data frame es organizándolas en un nuevo data frame
con los mismos nombres de las variables, y a continuación concatenarlas al data frame usando
la función rbind que ya usábamos para matrices.
> d . f1 = d . f1bk # Volvemos a la copia original
> d . f1
Sexo Edad Hermanos
1 Hombre 17 2
2 Hombre 18 0
3 Mujer 20 0
4 Hombre 18 1
5 Hombre 18 1
6 Hombre 18 1
7 Mujer 19 0
> nuevas . filas = data . frame ( Sexo = c ( " Hombre " ," Hombre " ) , Edad = c (18 ,18) ,
Hermanos = c (1 ,2) )
> d . f1 = rbind ( d . f1 , nuevas . filas )
> d . f1
Sexo Edad Hermanos
1 Hombre 17 2
2 Hombre 18 0
3 Mujer 20 0
4 Hombre 18 1
5 Hombre 18 1
5-18
6 Hombre 18 1
7 Mujer 19 0
8 Hombre 18 1
9 Hombre 18 2
> str ( d . f1 ) # Los tipos de las variables no han cambiado
’ data . frame ’: 9 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2 1 1
$ Edad : num 17 18 20 18 18 18 19 18 18
$ Hermanos : num 2 0 0 1 1 1 0 1 2
Para añadir una variable, basta especificar el valor de la columna correspondiente; por ejem-
plo, vamos a añadir a d.f1 una nueva variable llamada Nueva con el producto de la edad por
el número de hermanos:
> d . f1 $ Nueva = d . f1 $ Edad * d . f1 $ Hermanos
> d . f1
Sexo Edad Hermanos Nueva
1 Hombre 17 2 34
2 Hombre 18 0 0
3 Mujer 20 0 0
4 Hombre 18 1 18
5 Hombre 18 1 18
6 Hombre 18 1 18
7 Mujer 19 0 0
8 Hombre 18 1 18
9 Hombre 18 2 36
También podemos concatenar columnas a un data frame usando la función cbind; en este
caso, se puede añadir directamente la columna, sin necesidad de convertirla previamente en un
data frame. La variable añadida ha de tener la misma longitud que las variables del data frame
original; en caso contrario, se añadirán valores NA a las variables del data frame original o a la
variable que añadimos hasta completar la misma longitud.
> d . f1 = cbind ( d . f1 , Grado = rep ( " Biolog í a " ,9) )
> d . f1
Sexo Edad Hermanos Nueva Grado
1 Hombre 17 2 34 Biolog í a
2 Mujer 18 0 0 Biolog í a
3 Mujer 20 0 0 Biolog í a
4 Mujer 18 1 18 Biolog í a
5 Hombre 18 1 18 Biolog í a
6 Hombre 18 1 18 Biolog í a
7 Mujer 19 0 0 Biolog í a
8 Hombre 18 1 18 Biolog í a
9 Hombre 18 2 36 Biolog í a
> str ( d . f1 )
’ data . frame ’: 9 obs . of 5 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 2 1 1 1 2 1 1
$ Edad : num 17 18 20 18 18 18 19 18 18
$ Hermanos : num 2 0 0 1 1 1 0 1 2
$ Nueva : num 34 0 0 18 18 18 0 18 36
5-19
$ Grado : Factor w / 1 level " Biolog í a " : 1 1 1 1 1 1 1 1 1
Finalmente, hay que recordar que también podemos usar el editor de datos para añadir (o
eliminar) filas o columnas a un data frame.
Podemos usar este tipo de instrucciones para obtener las filas en un orden diferente del que
presentan en el data frame original.
> d . f1 [ c (2 ,3 ,1) , ]
Sexo Edad Hermanos
2 Hombre 18 0
3 Mujer 20 0
1 Hombre 17 2
También podemos seleccionar filas de un data frame mediante una condición lógica; en este
caso, nos quedaremos sólo con los individuos que satisfagan esta condición. Por ejemplo, si
queremos seleccionar los estudiantes de d.f1 de menos de 19 años, podemos usar la construcción
siguiente:
> d . f1 [ d . f1 $ Edad < 19 , ]
Sexo Edad Hermanos
1 Hombre 17 2
2 Hombre 18 0
4 Hombre 18 1
5 Hombre 18 1
5-20
6 Hombre 18 1
En cada caso, hemos obtenido un data frame. Vamos a llamar d.f.18 al primero.
> d . f .18= d . f1 [ d . f1 $ Edad < 19 , ]
> d . f .18
Sexo Edad Hermanos
1 Hombre 17 2
2 Hombre 18 0
4 Hombre 18 1
5 Hombre 18 1
6 Hombre 18 1
> str ( d . f .18)
’ data . frame ’: 5 obs . of 3 variables :
$ Sexo : Factor w / 2 levels " Hombre " ," Mujer " : 1 1 1 1 1
$ Edad : num 17 18 18 18 18
$ Hermanos : num 2 0 1 1 1
Como vemos, las columnas que son factores heredan en estos subdata frames todos los niveles
del factor original, aunque no aparezcan en el trozo que hemos extraído. Podemos borrar los
niveles sobrantes de todos los factores redefiniendo el data frame como el resultado de aplicarle
la función droplevels.
> d . f .18= droplevels ( d . f .18)
> str ( d . f .18)
’ data . frame ’: 5 obs . of 3 variables :
$ Sexo : Factor w / 1 level " Hombre " : 1 1 1 1 1
$ Edad : num 17 18 18 18 18
$ Hermanos : num 2 0 1 1 1
Naturalmente, el mismo tipo de construcción se puede utilizar para definir un data frame for-
mado sólo por algunas variables de la tabla original, o incluso por sólo algunas filas y columnas.
Si sólo queremos definir la subtabla quedándonos con algunas variables, basta aplicar el nombre
del data frame al vector de variables (sin necesidad de dejar el espacio en blanco seguido de
una coma que serviría para indicar que nos referimos a columnas y no a filas); por ejemplo,
con las cuatro instrucciones siguientes obtenemos cada vez el mismo resultado, un data frame
formado por las dos primeras variables de d.f1:
> d . f1 [ , c (1 ,2) ] # Con la coma
Sexo Edad
1 Hombre 17
2 Hombre 18
3 Mujer 20
5-21
4 Hombre 18
5 Hombre 18
6 Hombre 18
7 Mujer 19
> d . f1 [ c (1 ,2) ] # Sin la coma
Sexo Edad
1 Hombre 17
2 Hombre 18
3 Mujer 20
4 Hombre 18
5 Hombre 18
6 Hombre 18
7 Mujer 19
> d . f1 [ - 3] # Eliminamos la tercera columna
Sexo Edad
1 Hombre 17
2 Hombre 18
3 Mujer 20
4 Hombre 18
5 Hombre 18
6 Hombre 18
7 Mujer 19
> d . f1 [ c ( " Sexo " ," Edad " ) ] # Especificamos los nombres
Sexo Edad
1 Hombre 17
2 Hombre 18
3 Mujer 20
4 Hombre 18
5 Hombre 18
6 Hombre 18
7 Mujer 19
Esta construcción se puede usar también para reordenar las columnas de un data frame; por
ejemplo:
> d . f1 = d . f1 [ c ( " Edad " ," Hermanos " ," Sexo " ) ]
> d . f1
Edad Hermanos Sexo
1 17 2 Hombre
2 18 0 Hombre
3 20 0 Mujer
4 18 1 Hombre
5 18 1 Hombre
6 18 1 Hombre
7 19 0 Mujer
El paquete dplyr incluye la función select que amplía las posibilidades para especificar las
variables que queremos extraer de un data frame; por ejemplo:
select(data frame, starts_with("x")) extrae del data frame las variables cuyo nom-
bre empieza con la palabra x.
5-22
select(data frame, ends_with("x")) extrae del data frame las variables cuyo nombre
termina con la palabra x.
select(data frame, contains("x")) extrae del data frame las variables cuyo nombre
contiene en algún sitio la palabra x.
Para más información sobre otras habilidades de la función select, podéis consultar su Ayuda.
Como podéis constatar, hay muchas maneras de extraer una misma subtabla de un data
frame dado; veamos una última posibilidad, usando la función subset. La instrucción
subset(data frame, condición, select=columnas)
extrae del data frame las filas que cumplen la condición y las columnas especificadas en el
select; si queremos todas las filas, no hay que especificar ninguna condición, y si queremos
todas las columnas, no hace falta especificar el parámetro select. Así, si del data frame iris
queremos extraer un data frame formado sólo por las plantas de especie virginica, podemos
usar la instrucción siguiente:
> iris . vir = subset ( iris , Species == " virginica " )
> head ( iris . vir , 5)
Sepal . Length Sepal . Width Petal . Length Petal . Width Species
101 6.3 3.3 6.0 2.5 virginica
102 5.8 2.7 5.1 1.9 virginica
103 7.1 3.0 5.9 2.1 virginica
104 6.3 2.9 5.6 1.8 virginica
105 6.5 3.0 5.8 2.2 virginica
Fijaos en que las variables en la condición se especifican con su nombre, sin añadir antes el
nombre del data frame.
En este data frame, la variable Species es redundante, porque todos los individuos toman el
mismo valor; por lo tanto, podríamos haberla eliminado al construirlo.
> iris . vir = subset ( iris , Species == " virginica " , select =1:4)
> head ( iris . vir , 5)
5-23
Sepal . Length Sepal . Width Petal . Length Petal . Width
101 6.3 3.3 6.0 2.5
102 5.8 2.7 5.1 1.9
103 7.1 3.0 5.9 2.1
104 6.3 2.9 5.6 1.8
105 6.5 3.0 5.8 2.2
Las filas de este data frame han heredado los identificadores del data frame iris. Si esto nos
molesta, los podemos cambiar.
> rownames ( iris . vir ) =1: length ( rownames ( iris . vir ) )
> head ( iris . vir , 5)
Sepal . Length Sepal . Width Petal . Length Petal . Width
1 6.3 3.3 6.0 2.5
2 5.8 2.7 5.1 1.9
3 7.1 3.0 5.9 2.1
4 6.3 2.9 5.6 1.8
5 6.5 3.0 5.8 2.2
5-24
> D = data . frame ( V1 = c (1 ,2 , NA ,3) , V2 = c (2 ,5 ,2 , NA ) )
> D
V1 V2
1 1 2
2 2 5
3 NA 2
4 3 NA
> sapply (D , FUN = mean )
V1 V2
NA NA
> sapply (D , FUN = mean , na . rm = TRUE )
V1 V2
2 3
A menudo querremos aplicar una función a variables de un data frame clasificadas por los
niveles de un, o más de un, factor; esto se puede hacer con la instrucción aggregate, cuya
sintaxis básica es
aggregate(variable(s)˜factor(es), data=data frame, FUN=función).
El resultado será un data frame.
Por ejemplo, si queremos calcular las medias de las longitudes de los pétalos de las flores de
cada una de las tres especies representadas en la tabla iris, podemos entrar la instrucción
siguiente:
> aggregate ( Petal . Length ~ Species , data = iris , FUN = mean , na . rm = TRUE )
Species Petal . Length
1 setosa 1.462
2 versicolor 4.260
3 virginica 5.552
Hemos añadido na.rm=TRUE dentro del aggregate para que no tenga en cuenta los NA al calcular
la media, por si acaso. Observad que el resultado es un data frame con variables Species y
Petal.Length. Cada fila corresponde a un nivel del factor Species.
Si queremos aplicar la función a más de una variable, tenemos que agruparlas a la izquierda
de la tilde con cbind.
> aggregate ( cbind ( Petal . Length , Petal . Width ) ~ Species , data = iris ,
FUN = mean )
Species Petal . Length Petal . Width
1 setosa 1.462 0.246
2 versicolor 4.260 1.326
3 virginica 5.552 2.026
Si queremos separar las variables mediante más de un factor, tenemos que agruparlos a la
derecha de la tilde con signos +: factor1+factor2+. . . Veamos un ejemplo. El paquete alr4
contiene la tabla de datos Rateprof, con los resultados globales de la evaluación de un grupo
de profesores universitarios por parte de sus estudiantes. Algunas de sus variables son: gender,
el sexo del profesor; pepper, que indica si en las encuestas se le ha considerado mayoritariamente
atractivo o no; y clarity y easiness, que valoran, entre 1 y 5, la claridad de exposición y la
5-25
accesibilidad del profesor, respectivamente. Vamos a calcular las medias de estas dos últimas
variables agrupándolas por sexo y atractivo.
> # Instalamos y cargamos el paquete " alr4 "
...
> aggregate ( cbind ( clarity , easiness ) ~ gender + pepper , data = Rateprof ,
FUN = mean )
gender pepper clarity easiness
1 female no 3.341391 3.147606
2 male no 3.451456 2.999056
3 female yes 4.345082 3.599128
4 male yes 4.371824 3.689741
Observamos que tanto la claridad como la accesibilidad medias de los profesores atractivos de
ambos sexos son considerablemente mayores que las de sus colegas considerados no atracti-
vos. Además, en promedio, se considera a los profesores no atractivos menos accesibles que a
las profesoras no atractivas, y a los profesores atractivos más accesibles que a las profesoras
atractivas.
Si ya hubiera existido una variable definida con el mismo nombre que una variable del data
frame al que aplicamos attach, hubiéramos obtenido un mensaje de error al ejecutar esta
función, y no se hubiera reescrito la variable global original.
La función detach devuelve la situación original, eliminando del entorno global las variables
del data frame.
> detach ( iris )
> Petal . Length
Error : object ’ Petal . Length ’ not found
5-26
5.9. «Big data» frames con data.table (opcional)
Aunque la manera usual de trabajar en R con tablas de datos es en forma de data frames, su
estructura interna los hace poco eficientes a la hora de manejar las tablas de datos de millones
de entradas que empiezan a ser habituales en estos tiempos de proyectos big data. Una solución
es usar la clase de objetos para definir tablas de datos que incorpora el paquete data.table;
para distinguirlos de los data frames, llamaremos a estos objetos data tables.
Un data table vendría a ser un data frame enriquecido, con la particularidad de que sus
filas no tienen identificadores; en particular se pueden usar con ellos todas las construcciones y
funciones para data frames, pero esto no es lo recomendable, puesto que entonces no se gana
nada usándolos. Los data tables ofrecen unas construcciones específicas para extraer subtablas,
añadir, borrar o modificar variables, y aplicar operaciones a variables, que son mucho más
eficientes que las propias de los data frames; su sintaxis es muy diferente de la usada en matrices
o data frames, pero vale la pena «cambiar el chip» si necesitáis trabajar con tablas realmente
grandes.
Se puede crear un data table de varias maneras:
Aplicando la función data.table a un data frame. Como hemos comentado, las filas
de los data tables no tienen identificadores; si las del data frame tenían identificadores
que no fueran números correlativos, se copian como una variable extra llamada rn (de
rownames).
Importando un fichero externo con la función fread, exactamente igual como si usáramos
read.table. Algunas ventajas de fread son que detecta automáticamente los separadores
de columnas y si el fichero contiene o no una primera fila con los nombres de las variables,
aunque su ventaja principal es la rapidez: tablas de varios Gb, que con read.table
tardan varias horas en importarse, con fread tardan pocos minutos. Es importante tener
en cuenta que, al contrario que read.table, la función fread importa por defecto las
columnas de palabras como tales, y no como factores: esto se puede cambiar añadiendo
stringsAsFactors=TRUE.
5-27
5: B 3 1.6 5.3
6: A 6 7.1 7.8
7: C 5 3.2 6.3
8: C 2 - 3.5 6.3
9: A 4 - 2.7 6.3
10: A 9 1.8 7.5
> str ( DT )
Classes ’ data . table ’ and ’ data . frame ’: 10 obs . of 4 variables :
$ Letras : chr " A " " B " " A " " A " ...
$ Var1 : num 3 7 5 9 3 6 5 2 4 9
$ Var2 : num 2.3 5.2 6.4 - 2.2 1.6 7.1 3.2 - 3.5 - 2.7 1.8
$ Var3 : num 6.8 6.4 5.5 6.2 5.3 7.8 6.3 6.3 6.3 7.5
- attr ( * , " . internal . selfref " ) = < externalptr >
Una de las operaciones específicas más útiles para data tables es la creación de índices (keys)
a partir de una o varias variables; estos índices sirven para identificar las filas y agilizan las
búsquedas y extracciones de filas. La manera de indexar un data table es mediante la instrucción
setkey(data table, variable1, variable2, ...).
El resultado visible de esta instrucción es que R reordena las filas del data table en orden
creciente de la variable1 y, en caso de empate, de la variable2, y así sucesivamente; las variables
se han de entrar con su nombre sin entrecomillar. Fijaos también en que no igualamos el data
table al resultado de setkey, simplemente le aplicamos esta función.
> setkey ( DT , Letras , Var3 )
> DT
Letras Var1 Var2 Var3
1: A 5 6.4 5.5
2: A 9 - 2.2 6.2
3: A 4 - 2.7 6.3
4: A 3 2.3 6.8
5: A 9 1.8 7.5
6: A 6 7.1 7.8
7: B 3 1.6 5.3
8: B 7 5.2 6.4
9: C 5 3.2 6.3
10: C 2 - 3.5 6.3
Vemos que las filas se han ordenado según su entrada en la variable Letras y, en los empates,
según su entrada en Var3. A partir de ahora, las búsquedas de filas con valores concretos en
estas variables se realizaría mediante búsqueda binaria usando esta ordenación. Este método
es mucho más eficiente que el que usaría un data frame: visitaría toda la primera variable
y generaría un vector de valores lógicos que indicara, para cada entrada, si cumple o no la
condición; visitaría toda la segunda variable y generaría un segundo vector de valores lógicos
que indicara, para cada entrada, si cumple o no la condición; generaría un tercer vector que
tuviera un TRUE en aquellas entradas que lo tuvieran en cada uno de los vectores anteriores;
finalmente, daría las filas correspondientes a los TRUE en este último vector.
Una vez hemos indexado un data table, podemos usar las variables que hemos definido como
índices para extraer filas. La conjunción de varias condiciones se indica con la función J; veamos
varios ejemplos, observad la sintaxis:
5-28
> DT [ " B " ] # Filas con una B en Letras
Letras Var1 Var2 Var3
1: B 3 1.6 5.3
2: B 7 5.2 6.4
> DT [ c ( " B " ," C " ) ] # Filas con una B o una C en Letras
Letras Var1 Var2 Var3
1: B 3 1.6 5.3
2: B 7 5.2 6.4
3: C 5 3.2 6.3
4: C 2 - 3.5 6.3
> DT [ Letras > " A " ] # Filas con entrada mayor que A en Letras
Letras Var1 Var2 Var3
1: B 3 1.6 5.3
2: B 7 5.2 6.4
3: C 5 3.2 6.3
4: C 2 - 3.5 6.3
4: C 2 - 3.5 6.3
> DT [ J ( " A " ,c (6.2 ,6.3) ) ] # Filas con A en Letras y 6.2 o 6.3 en Var3
Letras Var3 Var1 Var2
1: A 6.2 9 - 2.2
2: A 6.3 4 - 2.7
La construcción [ ] en los data tables tiene un significado, y, por lo tanto, un uso diferente
que en los data frames; en general, el argumento dentro de los corchetes aplicados a un data
table tiene tres partes, separadas por comas:
La primera indica las filas, como ya hemos visto. Normalmente, para que el proceso
sea eficiente, impondremos condiciones sobre las filas usando las variables definidas para
indexar la tabla. Si dejamos esta posición vacía, se toman todas las filas.
En resumen,
DT[X, Y, by=Z]
equivale a: «En el data table DT, extrae las filas X y ejecuta Y sobre sus columnas agrupando
las filas según Z».
Veamos varios ejemplos:
5-29
> DT [ , Var1 ] # El contenido de Var1
[1] 5 9 4 3 9 6 3 7 5 2
> DT [ ,2] # Esto no es la segunda variable
[1] 2
> DT [ ,2 , with = FALSE ] # Esto s í
Var1
1: 5
2: 9
3: 4
4: 3
5: 9
6: 6
7: 3
8: 7
9: 5
10: 2
> DT [ , list ( Var1 , Var2 ) ] # Un data table con las variables Var1 y
Var2
Var1 Var2
1: 5 6.4
2: 9 - 2.2
3: 4 - 2.7
4: 3 2.3
5: 9 1.8
6: 6 7.1
7: 3 1.6
8: 7 5.2
9: 5 3.2
10: 2 - 3.5
> DT [ " A " , mult = " first " ] # La primera fila con A en Letras
Letras Var1 Var2 Var3
1: A 5 6.4 5.5
> DT [ " A " , mult = " last " ] # La ú ltima fila con A en Letras
Letras Var1 Var2 Var3
1: A 6 7.1 7.8
> DT [ " A " , sum ( Var1 ) ] # Suma de Var1 de las filas con una A en Letras
Letras V1
1: A 36
> DT [ , sum ( Var1 ) , by = Letras ] # Suma de Var1 agrupando seg ú n el valor
de Letras
Letras V1
1: A 36
2: B 10
3: C 7
> DT [ , list ( sumas1 = sum ( Var1 ) , medias1 = mean ( Var2 ) ) ,
by = list ( Letras , Var3 ) ] # Suma de Var1 y media de Var2 , agrupando
seg ú n Letras y Var3
Letras Var3 sumas1 medias1
1: A 5.5 5 6.40
2: A 6.2 9 - 2.20
5-30
3: A 6.3 4 - 2.70
4: A 6.8 3 2.30
5: A 7.5 9 1.80
6: A 7.8 6 7.10
7: B 5.3 3 1.60
8: B 6.4 7 5.20
9: C 6.3 7 - 0.15
View muestra el data frame al que se aplica en la ventana superior izquierda de RStudio.
5-31
head aplicado a un data frame y un número n, nos muestra las primeras n filas del data
frame (por defecto, 6).
tail aplicado a un data frame y un número n, nos muestra las n últimas filas del data
frame (por defecto, 6).
names sirve para obtener un vector con los nombres de las columnas de un data frame, y
también para modificar estos nombres.
rownames sirve para obtener un vector con los identificadores de las filas de un data frame,
y también para modificar estos identificadores.
dimnames sirve para obtener una list formada por el vector de los identificadores de
las filas y el vector de los nombres de las columnas de un data frame, y también para
modificar estos vectores simultáneamente.
read.csv, read.xls, read.xlsx (ambas del paquete xlsx), read.mtb y read.spss (am-
bas del paquete foreign) permiten importar en un data frame una tabla de datos en
formato CSV, XLS, XLSX, Minitab o SPSS, respectivamente.
data.frame crea un data frame con los vectores a los que se aplica. Algunos parámetros
importantes:
5-32
droplevels borra todos los niveles sobrantes de todos los factores de un data frame.
select, del paquete dplyr, permite definir un data frame con todas las variables de un da-
ta frame que empiecen por una secuencia de letras dada (con el parámetro starts_with),
que terminen por una secuencia de letras dada (con el parámetro ends_with), o que con-
tengan una secuencia de letras dada (con el parámetro contains).
subset(data frame, condición, select=columnas) define un data frame con las filas
del data frame que cumplen la condición y las columnas especificadas en el parámetro
select.
sapply(data frame, FUN=función) aplica la función a las columnas de un data frame.
aggregate sirve para aplicar una función a una o varias variables de un data frame
agrupando sus entradas por los niveles de uno o varios factores.
attach añade las variables de un data frame al entorno global de R.
detach deshace el efecto de attach.
data.table, del paquete data.table, crea un data table a partir de vectores o de un
data frame.
fread, del paquete data.table, permite importar un fichero externo en un data table.
setkey, del paquete data.table, indexa un data table.
[1, 2, by=3], aplicado a un data.table, extrae las filas especificadas en la posición 1
y ejecuta la expresión 2 sobre sus columnas, agrupando las filas según la condición 3.
5.11. Ejercicio
La tabla de datos pulse.txt que encontraréis en http://aprender.uib.es/Rdir/pulse.txt
recoge una serie de informaciones de tipo general (altura, peso, sexo, si corren, si fuman y su
nivel de actividad física: 1 si es bajo, 2 si es moderado y 3 si es alto) sobre algunos estudiantes
matriculados en un curso de estadística de la Universidad Estatal de Pensilvania hace unos
años. A estos estudiantes se les pidió que lanzasen una moneda al aire: a los que sacaron cara,
se les hizo correr un minuto sin moverse del sitio, y los que sacaron cruz, descansaron un minuto.
Todos los estudiantes (tanto los que corrieron como los que no) midieron sus pulsaciones por
minuto antes y después de este minuto de ejercicio o descanso, y estas medidas también aparecen
en esta tabla (en las variables PuBefor y PuAfter, respectivamente).
(a) ¿Cuántos estudiantes tomaron parte en este estudio? ¿Cuántos son hombres y cuántas
mujeres?
(b) Calculad el porcentaje medio de variación en el número de pulsaciones tras el minuto de
ejercicio o descanso de los estudiantes que corrieron (se indica con el valor yes en la variable
Ran.) y de los que no. ¿Hay mucha diferencia?
(c) Calculad el porcentaje medio de incremento en el número de pulsaciones tras el minuto de
ejercicio sólo para los estudiantes que corrieron, pero ahora distinguiendo los hombres de
las mujeres. ¿Cuál de los dos incrementos medios es mayor?
5-33
(d) Calculad el porcentaje medio de incremento en el número de pulsaciones tras el minuto de
ejercicio para los estudiantes que corrieron, pero ahora distinguiendo los estudiantes que
fuman de los que no. ¿Cuál de los dos incrementos medios es mayor?
(e) Calculad el número medio de pulsaciones antes del minuto de ejercicio o descanso de todos
los estudiantes, separados según su nivel de actividad física. ¿Se observa alguna diferencia
significativa?
5-34
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 4
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 6
Gráficos I
El objetivo de esta lección es introducir los aspectos básicos de los gráficos que se obtienen por
medio de la función plot. Muchos de los parámetros y funciones auxiliares que explicamos en
esta lección se podrán usar más adelante en otras funciones que producen tipos específicos de
gráficos en estadística descriptiva.
(2, 1), (5, 7), (6, 3), (3, 2), (4, 1),
basta entrar
> x = c (2 ,5 ,6 ,3 ,4)
> y = c (1 ,7 ,3 ,2 ,1)
> plot (x , y )
Figura 6.1. Gráfico básico de los puntos (2, 1), (5, 7), (6, 3),
(3, 2), (4, 1).
Cuando aplicamos plot a un solo vector (x1 , . . . , xn ), R produce el gráfico de los puntos
(1, x1 ), . . . , (n, xn ).
Es decir, si el vector tiene longitud n, plot(x) es una abreviatura de plot(1:n, x). Así, la
Figura 6.2 se obtiene con la siguiente instrucción:
> plot (2^(1:5) )
30
25
20
2^(1:5)
15
10
5
1 2 3 4 5
Index
La función plot también sirve para dibujar el gráfico de una función definida mediante
function. Por ejemplo, la gráfica de la función y = x2 entre x = 0 y x = 1 de la Figura 6.3 se
obtiene con el código siguiente:
> f = function ( x ) { x ^2}
> plot ( f )
Para modificar las etiquetas de los ejes de coordenadas, tenemos que usar xlab="etiqueta"
e ylab="etiqueta".
Los valores de estos parámetros se tienen que entrar entre comillas o, si son fórmulas mate-
máticas, aplicarles la función expression( ), para que aparezcan en un formato matemático
6-2
1.0
0.8
0.6
f
0.4
0.2
0.0
más adecuado. Por ejemplo, suponemos que conocéis la sucesión (Fn )n>0 de los números de
Fibonacci
1, 1, 2, 3, 5, 8, 13, 21, . . . ,
que empieza con F0 = F1 = 1 y a partir de aquí cada término es la suma de los dos anteriores.
Esta sucesión está definida por la fórmula
p p ⌘
1 ⇣ 1 + 5 ⌘n+1 1 ⇣1 5 n+1
Fn = p · p · para todo n > 0.
5 2 5 2
Para comprobarlo para algunos valores, vamos a generar la parte inicial (Fn )n=0,...,30 de la
sucesión definida por esta fórmula.
> n =0:30
> Fib =(1 / sqrt (5) ) * ((1+ sqrt (5) ) / 2) ^( n +1) -
(1 / sqrt (5) ) * ((1 - sqrt (5) ) / 2) ^( n +1)
> Fib
[1] 1 1 2 3 5 8 13
[8] 21 34 55 89 144 233 377
[15] 610 987 1597 2584 4181 6765 10946
[22] 17711 28657 46368 75025 121393 196418 317811
[29] 514229 832040 1346269
Para dibujar los pares (n, Fn )n=0,...,30 en un gráfico titulado «Números de Fibonacci», con el
eje de abscisas etiquetado con n y el eje de ordenadas etiquetado con Fn igualado a su fórmula
explícita, podemos entrar la instrucción siguiente:
> plot (n , Fib , xlab = " n " , main = " N ú meros de Fibonacci " ,
ylab = expression ( F [ n ]==(1 / sqrt (5) ) * ((1+ sqrt (5) ) / 2) ^( n +1)
- (1 / sqrt (5) ) * ((1 - sqrt (5) ) / 2) ^( n +1) ) )
El resultado es la Figura 6.4. Si os interesa información sobre cómo escribir las fórmulas dentro
de expression, podéis consultar la Ayuda de plotmath.
6-3
Números de Fibonacci
1200000
5 )((1 − 5 ) 2)(n+1)
0 5 10 15 20 25 30
Vale, hemos hecho trampa. Si habéis ejecutado la instrucción anterior tal cual, la etiqueta
del eje de ordenadas os habrá quedado demasiado cerca del borde, y, por ejemplo, no se verá
la línea horizontal superior de las raíces cuadradas. Para solucionarlo, hemos modificado los
márgenes de la figura. Estos márgenes, especificados en números de líneas, se controlan con el
parámetro mar, pero este parámetro no se puede especificar dentro de plot, sino antes, como
argumento de la función par, que sirve para modificar el aspecto general de los gráficos. Así
que en realidad, las instrucciones que han producido el gráfico han sido:
> viejo . par = par () # Guardamos los valores antiguos de par
> par ( mar = c (5 ,4.5 ,4 ,2) +0.1) # Modificamos los m á rgenes
> plot (n , Fib , xlab = " n " , main = " N ú meros de Fibonacci " ,
ylab = expression ( F [ n ]==(1 / sqrt (5) ) * ((1+ sqrt (5) ) / 2) ^( n +1)
- (1 / sqrt (5) ) * ((1 - sqrt (5) ) / 2) ^( n +1) ) )
> par = viejo . par # Volvemos a los valores anteriores de par
Consultad la Ayuda de par para conocer cómo modificar otros parámetros gráficos.
Cómo podéis ver, por defecto plot dibuja los puntos como círculos vacíos. Esto se puede
cambiar especificando el signo con el parámetro pch, que puede tomar como valor cualquier
número natural entre 0 y 25. Con pch=0 obtenemos cuadrados, pch=1 produce los círculos que
ya habéis visto (es el valor por defecto), pch=2 produce triángulos, etc. La Figura 6.5 muestra
los signos correspondientes a los valores de pch. También se pueden usar letras para representar
los puntos: hay que especificarlo igualando el parámetro pch a la letra entre comillas.
El tamaño de estos signos se puede modificar mediante el parámetro cex igualado al factor de
escalado: cex=2 produce signos el doble de grandes que los que obtenemos por defecto, cex=0.5
produce signos de la mitad de tamaño, etc. Por ejemplo,
6-4
> x = c (2 ,5 ,6 ,3 ,4) ; y = c (1 ,7 ,3 ,2 ,1)
> plot (x , y , pch =20 , cex =3)
7
6
5
4
y
3
2
1
2 3 4 5 6
Figura 6.6. Gráfico de los puntos (2, 1), (5, 7), (6, 3), (3, 2),
(4, 1) con puntos de tamaño triple.
El color de los puntos se puede especificar mediante el parámetro col igualado al nombre
del color en inglés. La paleta de colores de R consta de 502 colores diferentes. Podéis encontrar
una presentación muy clara de esta paleta en el documento Rcolor.pdf que encontraréis en el
repositorio del curso o en su url original
http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf
Así, por ejemplo,
> # Con los ú ltimos valores de x e y
> plot (x , y , col = " red " , pch =15)
El parámetro type permite indicar el tipo de gráfico que queremos producir. El valor del
parámetro se tiene que entrar entre comillas y puede ser:
"p", para dibujar los puntos como simples puntos, como hasta ahora; es el valor por
defecto.
"l", para dibujar los puntos unidos por líneas rectas sin que se vean los puntos.
6-5
7
7
6
6
5
5
4
4
y
y
3
3
2
2
1
1
2 3 4 5 6 2 3 4 5 6
x x
Figura 6.7. Dos gráficos de los puntos (2, 1), (5, 7), (6, 3), (3, 2),
(4, 1).
"b", para dibujar los puntos unidos por líneas rectas de manera que se vean los puntos
(los dibuja ambos, both: rectas y puntos), pero sin que las rectas entren dentro de los
signos que representan los puntos.
"o", que es como "b", pero ahora las rectas sí que entran dentro de los puntos.
"h", para dibujar líneas verticales desde el eje de abscisas a cada punto (un histograma
de líneas).
"n", para no dibujar los puntos, sólo el exterior del gráfico (ejes, título, etc.).
El parámetro col especifica el color tanto de los puntos como de las líneas.
6-6
con p con l
8
7
7
6
6
5
5
y
y
4
4
3
3
2
2
2 4 6 8 10 2 4 6 8 10
x x
con b con o
●
8
8
7
7 ●
6
6
5
5
y
●
4
●
3
●
2
2 4 6 8 10 2 4 6 8 10
x x
con h con s
8
8
7
7
6
6
5
5
y
y
4
4
3
3
2
2 4 6 8 10 2 4 6 8 10
x x
con n
8
7
6
5
y
4
3
2
2 4 6 8 10
Figura 6.8. Gráficos de los puntos (1, 2), (3, 8), (5, 4), (7, 6),
(11, 3) con los distintos valores del parámetro type.
6-7
El tipo de línea se puede especificar con el parámetro lty igualado a uno de los valores
siguientes: "solid", o 1, (el valor por defecto, que produce una línea continua); "dashed",
o 2, (que produce una línea discontinua); "dotted", o 3, (que produce una línea de
puntos); "dotdash", o 4, (que produce una línea que alterna puntos y rayas).
El grosor se puede especificar con el parámetro lwd. Con lwd=x imponemos que el grosor
de las líneas sea x veces su valor por defecto.
Así, los dos gráficos de la Figura 6.9 se obtienen con el código siguiente:
> x = c (1 ,3 ,5 ,7 ,11)
> y = c (2 ,8 ,4 ,6 ,3)
> plot (x , y , type = " o " , pch =19 , lty = " dotted " )
> plot (x , y , type = " o " , pch =19 , lty = " dashed " , lwd =2)
8
8
7
7
6
6
5
5
y
y
4
4
3
3
2
2 4 6 8 10 2 4 6 8 10
x x
Figura 6.9. Gráficos de los puntos (1, 2), (3, 8), (5, 4), (7, 6),
(11, 3) con distintos valores de los parámetros lty y lwd.
Si el argumento de plot son dos vectores, por norma general los rangos de los ejes de coorde-
nadas van, por defecto, del mínimo al máximo de los vectores correspondientes. Si su argumento
es una función f , por defecto el rango del eje de abscisas es el intervalo [0, 1] y el rango del eje
de ordenadas va del valor mínimo de f sobre el rango de las abscisas al máximo. Si queremos
modificar estos rangos, tenemos que usar los parámetros xlim e ylim, igualados cada uno a un
vector de entradas los extremos del rango.
Veamos un ejemplo de uso de estos parámetros:
> f = function ( x ) { x * log ( x ) }
> plot ( f ) # Rangos por defecto
> plot (f , xlim = c (1 ,10) ) # Fijamos el rango de x
> plot (f , xlim = c (1 ,10) , ylim = c (0 ,50) ) # Fijamos los rangos de x e y
6-8
0.0
50
20
40
-0.1
15
30
f
f
-0.2
10
20
5
10
-0.3
0
0.0 0.2 0.4 0.6 0.8 1.0 2 4 6 8 10 2 4 6 8 10
x x x
imponemos que R dibuje m + 1 marcas igualmente espaciadas entre los puntos n1 y n2 del eje
de abscisas. La sintaxis para yaxp es la misma. Estas instrucciones no definen los rangos de
los ejes de coordenadas, que se han de especificar con xlim e ylim si se quieren modificar. Por
ejemplo,
> f = function ( x ) { x * log ( x ) }
> plot (f , xlim = c (1 ,10) )
> plot (f , xlim = c (1 ,10) , xaxp = c (1 ,10 ,9) )
> plot (f , xlim = c (1 ,10) , xaxp = c (1 ,10 ,9) , ylim = c (0 ,25) ,
yaxp = c (0 ,25 ,10) )
20
15
15
f
f
10
10
7.5
5
5.0
2.5
0.0
0
2 4 6 8 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
x x x
La función plot dispone de muchos otros parámetros, que podéis consultar en la Ayuda de
plot y las de las funciones que se citan en su sección See Also. Son especialmente útiles los
parámetros que se explican en la Ayuda de par.
6-9
uno, usando las funciones adecuadas. En esta sección veremos algunas funciones que permiten
añadir elementos a un gráfico.
Antes de empezar, queremos avisaros de algo muy importante. Cuando añadimos objetos a
un gráfico, ya no podemos modificar su diseño general. Por ejemplo, los rangos de coordenadas
de los ejes del gráfico final o sus etiquetas serán los del primer gráfico, aunque especifiquemos
valores nuevos en el argumento de las funciones que usemos para añadir objetos.
La instrucción points(x,y) añade un punto de coordenadas (x, y) al gráfico activo. Podemos
declarar el color, el signo etc., de este punto mediante los parámetros usuales. (¡Atención! La
instrucción es points, no point.) Por ejemplo,
> f = function ( x ) { x ^2}
> plot (f , xlim = c ( - 3 ,3) )
> points (0 ,0 , pch =19)
4
2
0
-3 -2 -1 0 1 2 3
La función points también sirve para añadir una familia de puntos. En este caso, hay que
entrar en el argumento de points(x,y) la lista x de sus primeras coordenadas y la lista y de
sus segundas coordenadas, como lo haríamos en plot. Veamos algunos ejemplos:
> f = function ( x ) { x ^2}
> plot (f , xlim = c (0 ,10) )
> points (0:10 ,(0:10) ^2 , pch =19)
dibuja un trozo de la parábola y = x2 y añade los puntos (n, n2 )n=0,...,10 , produciendo la Figura
6.13.(a), y el código
> n =0:20
> x =1.3^ n - 2 * 0.8^ n
> y =0.2 * 1.3^ n +1.7 * 0.8^ n
> plot (n ,x , col = " blue " )
6-10
> points (n ,y , pch =19 , col = " red " )
primero dibuja los puntos (n, 1.3n 2 · 0.8n )n=0,...,20 como circulitos azules vacíos, y después
añade los puntos (n, 0.2 · 1.3n + 1.7 · 0.8n )n=0,...,20 como circulitos rojos llenos, produciendo la
Figura 6.13.(b).
100
80
60
f
40
20
0
0 2 4 6 8 10
(a) (b)
Figura 6.13. (a) Gráfico de la función y = x2 y los puntos
(n, n2 )n=0,...,10 ; (b) Gráfico de las familias de puntos (n, 1.3n
2 · 0.8n )n=0,...,20 y (n, 0.2 · 1.3n + 1.7 · 0.8n )n=0,...,20 usando
diferentes signos para cada familia.
La función abline sirve para añadir una recta; ya la usamos en la Lección 2 para añadir a
un gráfico una recta de regresión calculada con lm. Esta función tiene tres variantes:
Podemos especificar las características de estas rectas, como su grosor, su estilo o su color,
mediante los parámetros pertinentes. Por ejemplo,
> f = function ( x ) { x ^2}
> plot (f , xlim = c ( - 3 ,3) , col = " red " )
> points (0 ,0 , pch =19 , col = " blue " )
> points (1 ,1 , pch =19 , col = " blue " )
> abline ( v =1 , lty = " dashed " )
> abline ( h =0 , lty = " dotted " )
> abline ( - 1 ,2 , lty = " dotdash " )
6-11
8
6
f
4
2
0
-3 -2 -1 0 1 2 3
produce la Figura 6.14. En este caso, hemos añadido a la gráfica de la parábola y = x2 , dos
puntos en (0, 0) y (1, 1), la recta horizontal y = 0 de puntos, la recta vertical x = 1 discontinua
y la recta y = 1 + 2x de puntos y rayas.
Los parámetros v y h de abline se pueden igualar a vectores numéricos, en cuyo caso la
instrucción añade en un solo paso todas las rectas verticales u horizontales correspondientes,
todas del mismo estilo. Incluso se pueden combinar los parámetros v y h en una misma función:
> f = function ( x ) { x ^2}
> plot (f , xlim = c ( - 3 ,3) , col = " red " )
> abline ( h =0:9 , v = - 3:3 , lty = " dotted " )
4
2
0
-3 -2 -1 0 1 2 3
6-12
La instrucción
text(x,y, labels=...)
añade en el punto de coordenadas (x, y) el texto especificado como argumento de labels.
El texto se puede entrar entre comillas o en una expression. Esta instrucción admite un
parámetro opcional, pos, que puede tomar valores 1, 2, 3 o 4 y permite indicar la posición del
texto alrededor de las coordenadas (x, y): 1 significa abajo, 2 a la izquierda, 3 arriba y 4 a la
derecha. Sin especificar pos (o, equivalentemente, especificando pos=NULL, que es su valor por
defecto), el texto se sitúa centrado en el punto (x, y). El efecto de pos se ilustra en la Figura
6.16, producida con el código siguiente:
> plot (0 ,0 , pch =19 , xlab = " " , ylab = " " )
> text (0 ,0 , labels = " pos 1 " , pos =1)
> text (0 ,0 , labels = " pos 2 " , pos =2)
> text (0 ,0 , labels = " pos 3 " , pos =3)
> text (0 ,0 , labels = " pos 4 " , pos =4)
> points (0 ,0.5 , pch =19)
> text (0 ,0.5 , labels = " no pos " )
La función text se puede usar para añadir varios textos en un solo paso. En este caso, x e
y han de ser los vectores de abscisas y ordenadas de los puntos donde se añadirán los textos,
labels el vector de textos, y pos el vector de sus posiciones; en este vector, los textos que
queremos centrados en su posición se han de indicar con NULL. Así, la Figura 6.16 también se
hubiera podido obtener con el código siguiente, como podréis comprobar si lo ejecutáis:
> plot (0 ,0 , pch =19 , xlab = " " , ylab = " " )
> points (0 ,0.5 , pch =19)
> text ( rep (0 ,5) ,c ( rep (0 ,4) ,0.5) , pos = c (1 ,2 ,3 ,4 , NULL ) ,
labels = c ( " pos 1 " ," pos 2 " ," pos 3 " ," pos 4 " ," no pos " ) )
La instrucción lines(x,y), donde x = (xi )i=1,...,n e y = (yi )i=1,...,n son dos vectores numéricos
de la misma longitud, añade al gráfico una línea poligonal que une los puntos (xi , yi ) sucesivos.
El efecto es como si añadiéramos un plot(x,y, type=l). Naturalmente, la apariencia de las
6-13
líneas la podemos modificar con los parámetros usuales de grosor, color, estilo, etc. A modo de
ejemplo, la Figura 6.17 se obtiene con el código:
> f = function ( x ) { x ^2}
> plot (f , xlim = c (0 ,10) )
> lines (3.33 * (0:3) , (3.33 * (0:3) ) ^2 , lwd =2 , lty = " dashed " )
100
80
60
f
40
20
0
0 2 4 6 8 10
La instrucción curve con el parámetro add=TRUE permite añadir la gráfica de una curva a un
gráfico anterior. La curva se puede especificar mediante una expresión algebraica con variable
x, o mediante su nombre si la hemos definido antes. Por ejemplo,
> f = function ( x ) { x ^2}
> plot (f , xlim = c ( - 10 ,10) , xlab = " x " , ylab = " y " , main = " monomios " )
> curve ( x ^3 , lty = " dashed " , add = TRUE )
> curve ( x ^4 , lty = " dotted " , add = TRUE )
6-14
monomios
100
80
60
y
40
20
0
-10 -5 0 5 10
donde:
La posición indica donde queremos situar la leyenda, y puede ser o bien dos números
para especificar las coordenadas de su esquina superior izquierda, o bien una de las pa-
labras siguientes: "bottomright" (esquina inferior derecha), "bottom" (centrada abajo),
"bottomleft" (esquina inferior izquierda), "left" (centrada a la izquierda), "topleft"
(esquina superior izquierda), "top" (centrada arriba), "topright" (esquina superior de-
recha), "right" (centrada a la derecha) o "center" (en el centro del gráfico).
El vector legend contiene los nombres (entre comillas o aplicándoles expression) con
los que queremos identificar las curvas dentro de la leyenda.
Cada parámetro se usará para especificar el vector de sus valores sobre las diferentes
curvas, en el orden en el que aparecen en el vector legend e incluyendo los valores por
defecto. Se pueden usar varios parámetros. Si se quieren distinguir las curvas por su color,
obligatoriamente también se ha de especificar algún otro parámetro (si sólo se distinguen
por el color, se ha de añadir lty igualado al tipo de línea; por defecto, "solid").
Por ejemplo,
> curve ( x ^2 , xlim = c ( - 10 ,10) , xlab = " x " , ylab = " y " , main = " monomios " )
> curve ( x ^3 , lty = " dashed " , add = TRUE )
> curve ( x ^4 , lty = " dotted " , add = TRUE )
> legend ( " bottomleft " , legend = c ( expression ( x ^2) , expression ( x ^3) ,
expression ( x ^4) ) , lty = c ( " solid " , " dashed " , " dotted " ) )
produce el gráfico de la izquierda de la Figura 6.19. Observad que, aunque en el primer curve
no hemos especificado el parámetro lty, y ha tomado su valor por defecto, en el parámetro lty
del legend sí que hemos especificado su valor para la primera función.
Veamos otro ejemplo:
> curve (2 * x +3 , xlim = c ( - 10 ,10) , ylab = " " )
6-15
> curve (2 * x ^2+3 , col = " red " , lwd =2 , add = TRUE )
> curve (2 * x ^3+3 , col = " blue " , lwd =3 , add = TRUE )
> legend ( " topleft " , legend = c ( expression (2 * x +3) , expression (2 * x ^2+3) ,
expression (2 * x ^3+3) ) , lwd = c (1 ,2 ,3) , col = c ( " black " ," red " ," blue " ) )
monomios
100
2x + 3
2x2 + 3
20
2x3 + 3
80
10
60
y
0
40
20
-10
x2
x3
x4
0
-10 -5 0 5 10 -10 -5 0 5 10
x x
El código siguiente produce la Figura 6.20; en ella podemos observar que si el único parámetro
que especificamos dentro del legend es el color, no se ven las líneas dentro de la leyenda.
> curve (2 * x +3 , - 10 , 10 , ylab = " " )
> curve (2 * x ^2+3 , col = " red " , add = TRUE )
> curve (2 * x ^3+3 , col = " blue " , add = TRUE )
> legend ( " topleft " , legend = c ( expression (2 * x +3) , expression (2 * x ^2+3) ,
expression (2 * x ^3+3) ) , col = c ( " black " ," red " ," blue " ) )
Si os interesan, también disponéis de las funciones segments (para añadir segmentos), arrows
(para añadir flechas), symbols (para añadir signos, como estrellas, termómetros, . . . ), polygon
(para añadir polígonos cerrados especificando sus vértices), etc. Consultad las Ayudas de estas
instrucciones para los detalles sobre cómo se usan.
par sirve para modificar el aspecto general de los gráficos. Consultad su Ayuda para
conocer todos los parámetros que se pueden especificar en esta función.
plot es la función genérica para producir gráficos. Sus dos usos principales (por el mo-
mento) son:
6-16
2x + 3
2x2 + 3
20
2x3 + 3
10
0
-10
-10 -5 0 5 10
Los parámetros pch, cex, col, bg, type, lty y lwd también se pueden usar en las funciones
que siguen.
abline añade una recta al gráfico activo. Algunos parámetros específicos importantes:
6-17
lines añade una línea poligonal al gráfico activo.
curve, con el parámetro add=TRUE, sirve para añadir la gráfica de una función al gráfico
activo. Esta función admite todos los parámetros de plot.
6.5. Ejercicio
ct
La función de Gompertz es G(t) = ae be , con a, b, c 2 R estrictamente positivos, y se usa para
modelar el crecimiento de tumores o de poblaciones con recursos limitados. Vamos a analizar
gráficamente el efecto de los parámetros a, b, c.
(b) Para estudiar el efecto de b, dibujad en un mismo gráfico tres funciones de Gompertz con
los mismos valores de a y c y diferentes valores de b. De nuevo, usad todo lo que consideréis
necesario para mejorar la comprensión del gráfico. A partir de este gráfico, ¿qué efecto
diríais que tiene el parámetro b sobre la gráfica de la función?
(c) Para estudiar el efecto de c, dibujad en un mismo gráfico tres funciones de Gompertz con
los mismos valores de a y b y diferentes valores de c. De nuevo, usad todo lo que consideréis
necesario para mejorar la comprensión del gráfico. A partir de este gráfico, ¿qué efecto
diríais que tiene el parámetro c sobre la gráfica de la función?
6-18
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 5
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 7
Descripción de datos cualitativos
Los datos cualitativos corresponden a observaciones sobre cualidades de un objeto o individuo,
tales como su especie o su sexo, que pueden ser iguales o diferentes, pero que no admiten
ningún otro tipo de comparación significativa: por ejemplo, para los que no tenga ningún sentido
preguntarse si uno es más grande que otro, ni efectuar operaciones aritméticas con ellos, aunque
estén representados por números. Llamaremos variable cualitativa a una lista de observaciones
de un tipo de datos cualitativos sobre un conjunto concreto de objetos, y niveles, como en los
factores, a los diferentes valores que pueden tomar estos datos; por ejemplo, los dos niveles de
una variable Sexo serían «Macho» y «Hembra», o sinónimos.
Con R, usaremos vectores y factores para representar variables cualitativas. Los factores nos
servirán para agrupar las observaciones según los niveles de la variable. De esta manera podre-
mos segmentar la población que representa la variable en grupos o subpoblaciones, asignando
un grupo a cada nivel, y podremos comparar el comportamiento de otras variables sobre estos
grupos.
7.1. Frecuencias
Los estadísticos básicos para datos cualitativos son sencillos: dada una variable cualitativa, para
cada uno de sus niveles podemos contar cuántos datos hay en ese nivel (la frecuencia absoluta
del nivel) y qué fracción del total representan (la frecuencia relativa del nivel) y nada más.
Ejemplo 7.1. Supongamos que se ha realizado un seguimiento a 20 personas ingresadas en
un geriátrico. Uno de los datos que se han recogido sobre estas personas ha sido su sexo. El
resultado ha sido una variable cualitativa formada por las 20 observaciones siguientes:
Mujer, Mujer, Hombre, Mujer, Mujer, Mujer, Mujer, Mujer, Hombre, Mujer, Hom-
bre, Hombre, Mujer, Mujer, Hombre, Mujer, Mujer, Mujer, Mujer, Hombre.
Sus dos niveles son «Hombre» y «Mujer». En esta variable hay 14 mujeres y 6 hombres. Por lo
tanto, éstas son las frecuencias absolutas de estos niveles. Puesto que en total hay 20 individuos,
sus frecuencias relativas son
6 14
Hombre: = 0.3, Mujer: = 0.7.
20 20
l 1 , l2 , . . . , l k .
x 1 , x2 , . . . , x n
los resultados que obtenemos. Cada una de estas observaciones xj toma como valor alguno de
los niveles li . Estas observaciones forman una variable cualitativa.
Así, en el ejemplo anterior tendríamos que l1 = Hombre y l2 = Mujer, que n = 20 (el número
de observaciones efectuadas), y x1 , . . . , x20 formarían la muestra de sexos.
Con estas notaciones:
La frecuencia absoluta del nivel lj en esta variable cualitativa, que denotaremos por nj ,
es el número de observaciones en las que el resultado xi es igual al nivel lj .
La Tabla 7.1 resume las frecuencias absolutas y relativas de la variable cualitativa del Ejem-
plo 7.1, con las notaciones que acabamos de introducir. Su moda es el nivel Mujer.
Sexo nj fj %
Hombre 6 0.3 30 %
Mujer 14 0.7 70 %
Total 20 1 100 %
Tabla 7.1. Frecuencias de la variable del Ejemplo 7.1.
Con R, la tabla de frecuencias absolutas de un vector que representa una variable cualitativa
se calcula con la función table.
> table ( x )
x
1 2 3 5 6
3 5 3 3 1
> table ( Respuestas )
1
Para simplificar, en lo que queda de sección, diremos vector para referirnos genéricamente tanto a un vector
como a un factor.
2
Sí, ya sabemos que «Sí» lleva acento ortográfico, pero procuramos evitar acentos y caracteres especiales en
los guiones de las lecciones, para que se puedan abrir sin problemas en cualquier ordenador.
7-2
Respuestas
No Si
5 3
El resultado de una función table es un objeto de datos de un tipo nuevo: una tabla de
contingencia, una table en el argot de R. Como vemos, al aplicar table a un vector obtenemos
una tabla unidimensional formada por una fila con los niveles de la variable y una segunda fila
donde, debajo de cada nivel, aparece su frecuencia absoluta en el vector.
Los nombres de las columnas de una tabla unidimensional se obtienen con la función names.
> names ( table ( x ) )
[1] " 1 " " 2 " " 3 " " 5 " " 6 "
> names ( table ( Respuestas ) )
[1] " No " " Si "
Habréis observado que en la table de un vector sólo aparecen los nombres de los niveles
presentes en el vector. Si el tipo de datos cualitativos usado tenía más niveles y queremos que
aparezcan explícitamente en la tabla (con frecuencia 0), hay que transformar el vector en un
factor con los niveles deseados.
> z = factor (x , levels =1:7) # Los niveles ser á n 1 ,2 ,3 ,4 ,5 ,6 ,7
> z
[1] 3 2 5 1 3 1 5 6 2 2 2 1 3 5 2
Levels : 1 2 3 4 5 6 7
> table ( z )
z
1 2 3 4 5 6 7
3 5 3 0 3 1 0
A efectos prácticos, podemos pensar que una tabla unidimensional es como un vector de
números donde cada entrada está identificada por un nombre: el de su columna. Para referirnos
a una entrada de una tabla unidimensional, podemos usar tanto su posición como su nombre
(entre comillas, aunque sea un número).
> table ( x ) [4] # La cuarta columna de table ( x )
5
3
> table ( x ) [ " 4 " ] # ¿ La columna de table ( x ) con nombre 4?
NA
> table ( x ) [ " 5 " ] # La columna de table ( x ) con nombre 5
5
3
> 3 * table ( x ) [2] # El triple de la segunda columna de table ( x )
2
15
Las tablas de contingencia aceptan la mayoría de las funciones explicadas para vectores.
> sum ( table ( x ) ) # Suma de las entradas de table ( x )
[1] 15
7-3
> sqrt ( table ( Respuestas ) ) # Ra í ces cuadradas de las entradas de
table ( Respuestas )
Respuestas
No Si
2.236068 1.732051
7-4
x
1 2 3 5 6
3 5 3 3 1
> names ( which ( table ( x ) ==3) )
[1] " 1 " " 3 " " 5 "
> names ( which ( table ( x ) ==2) )
character (0)
Ejemplo 7.2. Continuamos en la situación del Ejemplo 7.1. Para calcular las frecuencias y la
moda con R, haríamos lo siguiente:
> Sexo _ Ger = c ( " Mujer " ," Mujer " ," Hombre " ," Mujer " ," Mujer " ," Mujer " ,
" Mujer " ," Mujer " ," Hombre " ," Mujer " ," Hombre " ," Hombre " ," Mujer " ,
" Mujer " ," Hombre " ," Mujer " ," Mujer " ," Mujer " ," Mujer " ," Hombre " )
> t0 = table ( Sexo _ Ger )
> t0
Sexo _ Ger
Hombre Mujer
6 14
> prop . table ( t0 )
Sexo _ Ger
Hombre Mujer
0.3 0.7
> names ( which ( t0 == max ( t0 ) ) )
[1] " Mujer "
7-5
Si 2 1
> table ( Sexo , Respuestas )
Respuestas
Sexo No Si
H 3 2
M 2 1
El resultado es, en ambos casos, una tabla de contingencia como antes, pero ahora bidimensional,
puesto que cada entrada tiene dos dimensiones, una por cada variable, como en una matriz.
Como podemos ver, en una tabla bidimensional producida aplicando table a dos vectores,
los niveles del primer vector en el argumento definen las filas, y los del segundo, las columnas.
Así, en table(Respuestas,Sexo), las filas corresponden a las respuestas y las columnas a los
sexos. Para intercambiar filas por columnas, es decir, para «trasponer» la tabla sin tener que
recalcularla, podemos usar la misma función t que usamos para trasponer matrices:
> t ( table ( Respuestas , Sexo ) )
Respuestas
Sexo No Si
H 3 2
M 2 1
Como en el caso unidimensional, la función prop.table sirve para calcular tablas bidimen-
sionales de frecuencias relativas conjuntas de pares de variables. Pero en el caso bidimensional
tenemos dos tipos de frecuencias relativas, que definen, para cada par de variables, tres tablas
diferentes:
Las frecuencias relativas globales: para cada par de niveles, uno de cada variable, la
fracción de individuos que pertenecen a ambos niveles respecto del total de la muestra;
por ejemplo, la fracción de mujeres que han contestado que sí respecto del total de la
muestra sería la frecuencia relativa global del par (mujer, sí).
Las frecuencias relativas marginales: dentro de cada nivel de una variable, y para cada
7-6
nivel de la otra, la fracción de individuos que pertenecen al segundo nivel respecto del total
de la subpoblación definida por el primer nivel; por ejemplo, la fracción de mujeres que
han contestado que sí respecto del total de mujeres sería una frecuencia relativa marginal.
Dadas dos variables, se pueden calcular dos familias de frecuencias relativas marginales,
según cuál sea la variable que defina las subpoblaciones en las que calculemos las frecuen-
cias relativas de los niveles de la otra variable; no es lo mismo la fracción de mujeres que
han contestado que sí respecto del total de mujeres, que la fracción de mujeres que han
contestado que sí respecto del total de personas que han dado esta misma respuesta.
La tabla de frecuencias relativas globales se calcula aplicando sin más la función prop.table
a la table. Por lo que se refiere a las frecuencias relativas marginales, la variable que define las
subpoblaciones en las que las calculamos se indica con el parámetro margin. Así, con margin=1
especificamos que la variable que define las subpoblaciones es la primera, y que, por lo tanto,
las frecuencias relativas se calculan dentro de las filas; en cambio, con margin=2 especificamos
que la variable que define las subpoblaciones es la segunda, por lo que las frecuencias relativas
se calculan por columnas; margin=NULL es su valor por defecto y, por lo tanto, equivalente a
no especificar este parámetro.
> prop . table ( table ( Sexo , Respuestas ) ) # Global
Respuestas
Sexo No Si
H 0.375 0.250
M 0.250 0.125
> prop . table ( table ( Sexo , Respuestas ) , margin =1) # Por sexo
Respuestas
Sexo No Si
H 0.6000000 0.4000000
M 0.6666667 0.3333333
> prop . table ( table ( Sexo , Respuestas ) , margin =2) # Por respuesta
Respuestas
Sexo No Si
H 0.6000000 0.6666667
M 0.4000000 0.3333333
De esta manera:
7-7
> # Instalamos y cargamos el paquete " gmodels "
...
> CrossTable ( Sexo , Respuestas , prop . chisq = FALSE )
Cell Contents
|-------------------------|
| N |
| N / Row Total |
| N / Col Total |
| N / Table Total |
|-------------------------|
| Respuestas
Sexo | No | Si | Row Total |
-------------|-----------|-----------|-----------|
H | 3 | 2 | 5 |
| 0.600 | 0.400 | 0.625 |
| 0.600 | 0.667 | |
| 0.375 | 0.250 | |
-------------|-----------|-----------|-----------|
M | 2 | 1 | 3 |
| 0.667 | 0.333 | 0.375 |
| 0.400 | 0.333 | |
| 0.250 | 0.125 | |
-------------|-----------|-----------|-----------|
Column Total | 5 | 3 | 8 |
| 0.625 | 0.375 | |
-------------|-----------|-----------|-----------|
La leyenda Cell Contents explica los contenidos de cada celda de la tabla: en este caso, y
en orden descendente, la frecuencia absoluta N, la frecuencia relativa por filas, la frecuencia
relativa por columnas, y la frecuencia relativa global. Asimismo, se muestran las celdas de los
márgenes, con las frecuencias absolutas y relativas de cada fila (en la columna «Row Total»)
y cada columna (en la fila «Column Total»). Esta función dispone de muchos parámetros que
permiten modificar el contenido de las celdas y que podéis consultar en su Ayuda.
Una tabla de contingencia bidimensional es, básicamente, una matriz con algunos atributos
extra. En particular, podemos usar sobre estas tablas la mayoría de las funciones para matrices
que tengan sentido para tablas; por ejemplo, rowSums y colSums se pueden aplicar a una tabla
y suman sus filas y sus columnas, respectivamente:
> table ( Sexo , Respuestas )
Respuestas
Sexo No Si
H 3 2
M 2 1
> colSums ( table ( Sexo , Respuestas ) )
7-8
No Si
5 3
> rowSums ( table ( Sexo , Respuestas ) )
H M
5 3
> colSums ( prop . table ( table ( Sexo , Respuestas ) ) )
No Si
0.625 0.375
> rowSums ( prop . table ( table ( Sexo , Respuestas ) ) )
H M
0.625 0.375
También podemos usar sobre una tabla bidimensional (o, en general, multidimensional) la
función apply con la misma sintaxis que para matrices; véase la próxima sección.
tenemos un tercer vector con las nacionalidades de los individuos representados en estos dos
vectores:
> Pais = c ( " Francia " ," Alemania " ," Italia " ," Italia " ," Italia " ," Italia " ,
" Alemania " ," Francia " )
Podemos calcular entonces una tabla de frecuencias absolutas para las ternas (sexo, respuesta,
país).
> table ( Sexo , Respuestas , Pais )
, , Pais = Alemania
Respuestas
Sexo No Si
H 1 0
M 1 0
, , Pais = Italia
Respuestas
Sexo No Si
H 2 1
M 0 1
7-9
, , Pais = Francia
Respuestas
Sexo No Si
H 0 1
M 1 0
R muestra la tabla tridimensional que obtenemos como una lista de tablas bidimensionales
table(Sexo,Respuestas), separando la población según los niveles de la tercera variable. Si
no os gusta esta manera de visualizar una tabla tridimensional, una alternativa es usar la
función ftable, que la mostrará en lo que se llama formato plano:
> ftable ( Sexo , Respuestas , Pais )
Pais Alemania Italia Francia
Sexo Respuestas
H No 1 2 0
Si 0 1 1
M No 1 0 1
Si 0 1 0
Los parámetros row.vars y col.vars de ftable permiten especificar qué variables queremos
que aparezcan como filas o como columnas.
> ftable ( Sexo , Respuestas , Pais , col . vars = c ( " Sexo " ," Respuestas " ) )
Sexo H M
Respuestas No Si No Si
Pais
Alemania 1 0 1 0
Italia 2 1 0 1
Francia 0 1 1 0
Para referirnos a una entrada, o a una subtabla, de una tabla podemos usar los corchetes.
> table ( Sexo , Respuestas , Pais ) [ " H " ," Si " ," Italia " ]
[1] 1
> table ( Sexo , Respuestas , Pais ) [ , ," Italia " ]
Respuestas
Sexo No Si
H 2 1
M 0 1
> table ( Sexo , Respuestas , Pais ) [ ," Si " ," Italia " ]
H M
1 1
> table ( Sexo , Respuestas , Pais ) [ " M " , ," Italia " ]
No Si
0 1
7-10
a la tabla de frecuencias absolutas, y especificando con el parámetro margin las dimensiones,
o combinaciones de dimensiones, respecto de las que calculamos las frecuencias relativas. Si no
se especifica el parámetro margin, se obtiene la tabla de frecuencias relativas globales.
> prop . table ( table ( Sexo , Respuestas , Pais ) ) # Frecuencias relativas
globales
, , Pais = Alemania
Respuestas
Sexo No Si
H 0.125 0.000
M 0.125 0.000
, , Pais = Italia
Respuestas
Sexo No Si
H 0.250 0.125
M 0.000 0.125
, , Pais = Francia
Respuestas
Sexo No Si
H 0.000 0.125
M 0.125 0.000
> prop . table ( table ( Sexo , Respuestas , Pais ) , margin =3) # Frecuencias
relativas por pa í s
, , Pais = Alemania
Respuestas
Sexo No Si
H 0.50 0.00
M 0.50 0.00
, , Pais = Italia
Respuestas
Sexo No Si
H 0.50 0.25
M 0.00 0.25
7-11
, , Pais = Francia
Respuestas
Sexo No Si
H 0.00 0.50
M 0.50 0.00
Respuestas
Sexo No Si
H 1.0000000 0.0000000
M 1.0000000 0.0000000
, , Pais = Italia
Respuestas
Sexo No Si
H 0.6666667 0.3333333
M 0.0000000 1.0000000
, , Pais = Francia
Respuestas
Sexo No Si
H 0.0000000 1.0000000
M 1.0000000 0.0000000
En este caso:
Observad, también, que al aplicar prop.table al resultado de una ftable, la tabla resultante
sigue en formato plano.
Podemos aplicar funciones a filas o columnas de (sub)tablas de una tabla multidimensional
con la función apply, especificando en el parámetro FUN la función y en el parámetro MARGIN
la variable o combinación de variables a la que aplicamos la función (son las variables que
aparecerán en la tabla resultante). Observad las dos instrucciones siguientes y sus resultados:
7-12
> apply ( table ( Sexo , Respuestas , Pais ) , MARGIN = c (1 ,3) , FUN = sum )
Pais
Sexo Alemania Francia Italia
H 1 1 3
M 1 1 1
> apply ( table ( Sexo , Respuestas , Pais ) , MARGIN =2 , FUN = sum )
No Si
5 3
Hasta ahora hemos manipulado tablas de frecuencias que hemos construido nosotros mismos
a partir de variables cualitativas. Todo lo que hemos hecho con estas tablas se puede también
hacer con las tablas de frecuencias que lleva R predefinidas o que obtengamos de otra manera.
Por ejemplo, el objeto de datos HairEyeColor que lleva predefinido R es una tabla las frecuencias
absolutas, en una muestra de personas, de tres variables cualitativas: color de cabello (Hair),
color de los ojos (Eye) y sexo (Sex), en este orden.
> HairEyeColor
, , Sex = Male
Eye
Hair Brown Blue Hazel Green
Black 32 11 10 3
Brown 53 50 25 15
Red 10 10 7 7
Blond 3 30 5 8
, , Sex = Female
Eye
Hair Brown Blue Hazel Green
Black 36 9 5 2
Brown 66 34 29 14
Red 16 7 7 7
Blond 4 64 5 8
7-13
Blond Brown 3 4
Blue 30 64
Hazel 5 5
Green 8 8
Efectuemos algunas operaciones sobre esta tabla, para ilustrar como podemos trabajar con
ella:
> sum ( HairEyeColor ) # N ú mero total de individuos en la muestra
[1] 592
> HairEyeColor [ , ," Male " ] # Subtabla de hombres
Eye
Hair Brown Blue Hazel Green
Black 32 11 10 3
Brown 53 50 25 15
Red 10 10 7 7
Blond 3 30 5 8
Eye
Hair Brown Blue Hazel Green
Black 0.114695341 0.039426523 0.035842294 0.010752688
Brown 0.189964158 0.179211470 0.089605735 0.053763441
Red 0.035842294 0.035842294 0.025089606 0.025089606
Blond 0.010752688 0.107526882 0.017921147 0.028673835
, , Sex = Female
Eye
Hair Brown Blue Hazel Green
Black 0.115015974 0.028753994 0.015974441 0.006389776
Brown 0.210862620 0.108626198 0.092651757 0.044728435
Red 0.051118211 0.022364217 0.022364217 0.022364217
Blond 0.012779553 0.204472843 0.015974441 0.025559105
Eye
Hair Brown Blue Hazel Green
Black 0.4705882 0.5500000 0.6666667 0.6000000
Brown 0.4453782 0.5952381 0.4629630 0.5172414
Red 0.3846154 0.5882353 0.5000000 0.5000000
Blond 0.4285714 0.3191489 0.5000000 0.5000000
, , Sex = Female
Eye
7-14
Hair Brown Blue Hazel Green
Black 0.5294118 0.4500000 0.3333333 0.4000000
Brown 0.5546218 0.4047619 0.5370370 0.4827586
Red 0.6153846 0.4117647 0.5000000 0.5000000
Blond 0.5714286 0.6808511 0.5000000 0.5000000
Para cambiar el orden de las variables en una tabla multidimensional, se puede usar la ins-
trucción
aperm(tabla, perm=...),
igualando el parámetro perm a la lista de las variables en el orden deseado. Por ejemplo, si que-
remos una tabla equivalente a HairEyeColor, pero con primera variable Sex, segunda variable
Hair y tercera variable Eye, podemos hacer:
> aperm ( HairEyeColor , perm = c ( " Sex " , " Hair " , " Eye " ) )
, , Eye = Brown
Hair
Sex Black Brown Red Blond
Male 32 53 10 3
Female 36 66 16 4
, , Eye = Blue
Hair
Sex Black Brown Red Blond
Male 11 50 10 30
Female 9 34 7 64
, , Eye = Hazel
Hair
Sex Black Brown Red Blond
Male 10 25 7 5
Female 5 29 7 5
, , Eye = Green
Hair
Sex Black Brown Red Blond
Male 3 15 7 8
Female 2 14 7 8
7-15
http://aprender.uib.es/Rdir/bebenerg.txt.
Este fichero consiste en una tabla de datos con la siguiente información sobre 122 estudiantes
de la Escuela Politécnica Superior de la UIB: su sexo (variable sexo), el grado en el que están
matriculados (variable estudio) y si consumen habitualmente bebidas energéticas (variable
bebe). Como contiene letras acentuadas, conviene que uséis el parámetro encoding.
> Beb _ Energ = read . table ( " http : / / aprender . uib . es / Rdir / bebenerg . txt " ,
header = TRUE , encoding = " UTF - 8 " )
> str ( Beb _ Energ )
’ data . frame ’: 122 obs . of 3 variables :
$ estudio : Factor w / 4 levels " Inform á tica " ,..: 1 3 2 1 2 3 1 2 1
1 ...
$ bebe : Factor w / 2 levels " No " ," S í " : 1 1 2 2 1 1 2 1 1 1 ...
$ sexo : Factor w / 2 levels " Hombre " ," Mujer " : 2 1 2 1 2 2 1 1 1
1 ...
> head ( Beb _ Energ )
estudio bebe sexo
1 Inform á tica No Mujer
2 Matem á ticas No Hombre
3 Ing . Industrial S í Mujer
4 Inform á tica S í Hombre
5 Ing . Industrial No Mujer
6 Matem á ticas No Mujer
Esta tabla sólo sirve para ver la información, porque sus entradas son palabras.
> summary ( Beb _ Energ ) [ ,2]
Para calcular en un solo paso la table de cada variable, podemos usar la función sapply de
la manera siguiente:
> sapply ( Beb _ Energ , FUN = table )
$ estudio
$ bebe
7-16
No S í
97 25
$ sexo
Hombre Mujer
83 39
De esta manera, obtenemos una list cuyas componentes son las tablas que queríamos.
> sapply ( Beb _ Energ , FUN = table ) $ sexo
Hombre Mujer
83 39
> table ( Beb _ Energ $ sexo )
Hombre Mujer
83 39
bebe
estudio No S í
Inform á tica 30 7
Ing . Industrial 19 6
Matem á ticas 8 1
Telem á tica 10 2
, , sexo = Mujer
bebe
estudio No S í
Inform á tica 11 5
Ing . Industrial 10 2
Matem á ticas 6 1
Telem á tica 3 1
Otra opción es usar la función ftable, que produce la misma tabla de frecuencias pero en
formato plano.
7-17
> ftable ( Beb _ Energ )
sexo Hombre Mujer
estudio bebe
Inform á tica No 30 11
Sí 7 5
Ing . Industrial No 19 10
Sí 6 2
Matem á ticas No 8 6
Sí 1 1
Telem á tica No 10 3
Sí 2 1
La función \n dentro de una frase entrada entre comillas introduce un cambio de línea. El mismo
efecto se obtiene con un cambio de línea. Id con cuidado, porque ambos efectos se acumulan,
así que si cambiáis de línea después del \n, obtendréis una línea en blanco. Por otro lado, \"
escribe unas comillas en el texto entrado entre comillas.
Diagrama de barras de las frecuencias absolutas
de la variable "Sexo_Ger"
14
12
10
8
6
4
2
0
Hombre Mujer
7-18
rrespondiente. Así,
> x = c (3 ,2 ,5 ,1 ,3 ,1 ,5 ,6 ,2 ,2 ,2 ,1 ,3 ,5 ,2)
> Respuestas = c ( " No " ," No " ," Si " ," No " ," Si " ," No " ," No " ," Si " )
> barplot ( table ( x ) , main = " Diagrama de barras de frecuencias
absolutas de la variable \ " x \ " " )
> barplot ( prop . table ( table ( Respuestas ) ) , main = " Diagrama de barras
de frecuencias relativas \ n de la variable \ " Respuestas \ " " )
0.6
0.5
4
0.4
3
0.3
2
0.2
1
0.1
0.0
0
1 2 3 5 6 No Sí
¡Atención! Como pasaba con prop.table, el argumento de barplot ha de ser una tabla, y,
por consiguiente, se ha de aplicar al resultado de table o de prop.table, nunca al vector de
datos original.
Habréis observado que en las funciones barplot anteriores hemos usado el parámetro main
para poner título a los diagramas; en general, la función barplot admite los parámetros de
plot que tienen sentido en el contexto de los diagramas de barras: xlab, ylab, main, etc.
Los parámetros disponibles se pueden consultar en la Ayuda de barplot. Aquí sólo vamos a
comentar algunos.
Se pueden especificar los colores de las barras usando el parámetro col. Si se iguala a un solo
color, todas las barras serán de este color, pero también se puede especificar un color para cada
barra, igualando col a un vector de colores. Por ejemplo,
> barplot ( table ( Respuestas ) , col = c ( " green " ) )
> barplot ( table ( Respuestas ) , col = c ( " red " ," blue " ) )
producen, respectivamente, los dos diagramas de la Figura 7.3. En un diagrama con muchas
barras, es conveniente usar un esquema adecuado de colores para ellas. Para ello se puede usar
el paquete RColorBrewer, del que hablaremos en detalle en la Sección 11.5.
Una opción interesante es dibujar las barras horizontales en vez de verticales: para hacerlo,
se tiene que añadir el parámetro horiz=TRUE. Así, la Figura 7.4 se obtiene con la siguiente
instrucción:
7-19
5
5
4
4
3
3
2
2
1
1
0
0
No Sí No Sí
0 1 2 3 4 5
Si se aplica barplot a una tabla bidimensional, por defecto dibuja las barras de la segunda
variable cortadas por la frecuencia de la primera variable: se le llama un diagrama de barras
apiladas. Por ejemplo, las instrucciones siguientes producen la Figura 7.5.
> Respuestas = c ( " No " ," No " ," Si " ," No " ," Si " ," No " ," No " ," Si " )
> Sexo = c ( " M " ," M " ," M " ," H " ," H " ," H " ," H " ," H " )
> table ( Sexo , Respuestas )
Respuestas
Sexo No Si
H 3 2
M 2 1
> barplot ( table ( Sexo , Respuestas ) )
7-20
En un diagrama de barras apiladas, las barras globales corresponden a los niveles de la variable
que definen las columnas de la tabla, es decir, la segunda variable especificada dentro de table:
en el de la Figura 7.5, se trata de la variable Respuestas, de niveles No y Si. Cada una de
estas barras se divide verticalmente en sectores que representan los niveles de la otra variable,
en orden ascendente: en el ejemplo que nos ocupa, la zona inferior de cada barra representa el
nivel H de la variable Sexo y la zona superior su nivel M.
5
4
3
2
1
0
No Sí
En vez de organizar las barras de la primera variable en una sola barra vertical, se pueden
dibujar una junto a la otra añadiendo el parámetro beside=TRUE, obteniéndose de esta manera
un diagrama de barras por bloques. Así,
> barplot ( table ( Sexo , Respuestas ) , beside = TRUE )
produce el diagrama de barras de la izquierda de la Figura 7.6. En este diagrama, cada bloque
de barras representa un nivel de la variable de las columnas (No y Si), y en cada uno de estos
bloques las barras representan los niveles de las filas en su orden (en cada bloque, la barra de
la izquierda corresponde a H y la de la derecha a M).
Los diagramas de barras tienen que mostrar la información de la manera más adecuada. Por
ejemplo, en un diagrama de barras por bloques, si lo que nos interesa es la distribución de las
respuestas por sexo, los bloques de barras deberían corresponder a los sexos, y las barras dentro
de cada bloque a las respuestas. En este caso, convendría cambiar el orden de los vectores dentro
de la table a la que aplicamos barplot, o trasponer la tabla antes de aplicarle barplot.
Suele ser conveniente añadir a un diagrama de barras de dos variables una leyenda que indique
qué nivel representa cada sector (en los diagramas de barras apiladas) o cada barra (en los
diagramas de barras por bloques). Esto se puede realizar entrando el parámetro legend.text
igualado a TRUE, si no queremos modificar los nombres de los niveles de las filas, o igualado a
un vector con los nombres que les queremos asignar (en el orden que toque). Por ejemplo,
> barplot ( table ( Sexo , Respuestas ) , beside = TRUE , legend . text = TRUE )
7-21
3.0
3.0
H
M
2.5
2.5
2.0
2.0
1.5
1.5
1.0
1.0
0.5
0.5
0.0
0.0
No Sí No Sí
7-22
3.0
No
Yes
2.5
2.0
1.5
1.0
0.5
0.0
Men Women
> x = c (3 ,2 ,5 ,1 ,3 ,1 ,5 ,6 ,2 ,2 ,2 ,1 ,3 ,5 ,2)
> Respuestas = c ( " No " ," No " ," Si " ," No " ," Si " ," No " ," No " ," Si " )
> pie ( table ( x ) , main = " Diagrama circular de la variable \"x\"")
> pie ( table ( Respuestas ) , main = " Diagrama circular de la variable
\ " Respuestas \ " " )
No
2
1
3
5
Sí
7-23
diagramas circulares de la Figura 7.9.3
Otra representación de las tablas multidimensionales de frecuencias son los gráficos de mo-
saico. Estos gráficos se obtienen sustituyendo cada entrada de la tabla de frecuencias por una
región rectangular de área proporcional a su valor. En concreto, para obtener el gráfico de mo-
saico de una tabla bidimensional, se parte de un cuadrado de lado 1, primero se divide en barras
verticales de amplitudes iguales a las frecuencias relativas de una variable, y luego cada barra
se divide, a lo alto, en regiones de alturas proporcionales a las frecuencias relativas marginales
de cada nivel de la otra variable dentro del nivel correspondiente de la primera variable.
Un gráfico de mosaico de una tabla se obtiene con R aplicando la función plot a la tabla, o
también la función mosaicplot; esta última también se puede aplicar a matrices. Por ejemplo,
> Respuestas = c ( " No " ," No " ," Si " ," No " ," Si " ," No " ," No " ," Si " )
> Sexo = c ( " M " ," M " ," M " ," H " ," H " ," H " ," H " ," H " )
> plot ( table ( Sexo , Respuestas ) , main = " Gr á fico de mosaico de las
variables \ " Sexo \ " y \ " Respuestas \ " " )
7-24
Gráfico de mosaico de las variables "Sexo" y "Respuestas" Gráfico de mosaico de la tabla "HairEyeColor"
Brown
No
Respuestas
Eye
Blue
Sí
Hazel
Green
Sexo Hair
Además de sus parámetros usuales, la función plot admite algunos parámetros específicos
cuando se usa para producir el gráfico de mosaico de una tabla. Los más interesantes son:
col, que ya hemos usado en el gráfico de mosaico anterior, asigna colores a los niveles de
la última variable (en ese ejemplo, los sexos).
dir, igualado a un vector de direcciones "v" (vertical) y "h" (horizontal), sirve para
especificar la dirección de las barras de cada variable (por defecto, como hemos comentado,
alternan vertical y horizontal, empezando por vertical).
7-25
Gráfico de mosaico de la tabla "HairEyeColor"
Hair
Black Brown Red Blond
Brown
Eye
Blue
Green Hazel
H
Sexo
Sexo
M
País = España
Respuestas
No Yes
H
Sexo
M
7-26
7.8. Un ejemplo completo
Vamos a llevar a cabo un análisis completo de un ejemplo con lo que hemos aprendido en esta
lección. Como ya hemos comentado, el objeto de datos HairEyeColor que lleva predefinido
R es una tabla de frecuencias absolutas de tres variables cualitativas: color de cabello (Hair),
color de los ojos (Eye) y sexo (Sex). Vamos a extraer de esta tabla una tabla bidimensional
de frecuencias absolutas de las variables Eye y Hair, sin distinguir según el sexo. La manera
más sencilla de obtener esta tabla es combinando la función apply con as.table, para que
el resultado final sea una tabla de contingencia (sin esta instrucción, el resultado sería una
matriz), de la manera siguiente:
> HEC = as . table ( apply ( HairEyeColor , MARGIN = c (1 ,2) , FUN = sum ) )
> HEC
Eye
Hair Brown Blue Hazel Green
Black 68 20 15 5
Brown 119 84 54 29
Red 26 17 14 14
Blond 7 94 10 16
Vamos a traducir al castellano los nombres de las variables de esta tabla y de sus niveles.
Esto lo podemos llevar a cabo en un solo paso con la función dimnames que ya usamos sobre
data frames. El resultado de aplicar esta función a una table es una list cuyas componentes
son los niveles de cada variable.
> dimnames ( HEC )
$ Hair
[1] " Black " " Brown " " Red " " Blond "
$ Eye
[1] " Brown " " Blue " " Hazel " " Green "
Por lo tanto, para reescribir los nombres de las variables y sus niveles, basta redefinir esta list
de la manera siguiente:
> dimnames ( HEC ) = list ( Cabello = c ( " Negro " ," Casta ñ o " ," Rojo " ," Rubio " ) ,
Ojos = c ( " Marrones " ," Azules " ," Pardos " ," Verdes " ) )
> HEC
Ojos
Cabello Marrones Azules Pardos Verdes
Negro 68 20 15 5
Casta ñ o 119 84 54 29
Rojo 26 17 14 14
Rubio 7 94 10 16
Vamos a dibujar un diagrama de mosaico de esta tabla, para visualizar gráficamente sus
entradas.
> plot ( HEC , col = c ( " lightblue " ) , main = " Diagrama de mosaico de la tabla
bidimensional de frecuencias \ n de colores de cabellos y ojos " )
7-27
Obtenemos la Figura 7.13. A simple vista, vemos que las combinaciones de colores de cabellos
y ojos más frecuentes son los cabellos castaños con los ojos marrones, y los cabellos rubios con
los ojos azules.
Marrones
Ojos
Azules
Pardos
Verdes
Cabello
Los diagramas de barras producidos con la dos últimas instrucciones son los de la Figura 7.14.
Vemos que el color dominante de cabellos es el castaño, mientras que en el color de ojos el
marrón y el azul están prácticamente empatados.
7-28
Frecuencias relativas de colores de ojos Frecuencias relativas de colores de cabello
0.4
0.5
0.4
0.3
0.3
0.2
0.2
0.1
0.1
0.0
0.0
Brown Blue Hazel Green Negro Castaño Rojo Rubio
Pasamos ahora a calcular las tablas de frecuencias relativas y dibujar los dos diagramas de
barras de las frecuencias relativas marginales.
> round ( prop . table ( HEC ) , 3) # Frec . rel . globales
Ojos
Cabello Marrones Azules Pardos Verdes
Negro 0.115 0.034 0.025 0.008
Casta ñ o 0.201 0.142 0.091 0.049
Rojo 0.044 0.029 0.024 0.024
Rubio 0.012 0.159 0.017 0.027
> round ( prop . table ( HEC , margin =1) , 3) # Frec . rel . por filas
Ojos
Cabello Marrones Azules Pardos Verdes
Negro 0.630 0.185 0.139 0.046
Casta ñ o 0.416 0.294 0.189 0.101
Rojo 0.366 0.239 0.197 0.197
Rubio 0.055 0.740 0.079 0.126
> round ( prop . table ( HEC , margin =2) , 3) # Frec . rel . por columnas
Ojos
Cabello Marrones Azules Pardos Verdes
Negro 0.309 0.093 0.161 0.078
Casta ñ o 0.541 0.391 0.581 0.453
Rojo 0.118 0.079 0.151 0.219
Rubio 0.032 0.437 0.108 0.250
> barplot ( prop . table ( HEC , margin =1) , beside = TRUE , legend . text = TRUE ,
col = c ( " black " ," brown " ," red " ," gold " ) , ylim = c (0 ,0.8) ,
main = " Frecuencias relativas de colores de cabello \ n en cada
color de ojos " )
> barplot ( t ( prop . table ( HEC , margin =2) ) , beside = TRUE ,
legend . text = TRUE , ylim = c (0 ,0.6) ,
col = c ( " burlywood4 " ," lightblue " ," orange3 " ," lightgreen " ) ,
main = " Frecuencias relativas de colores de ojos \ n en cada
7-29
color de cabellos " )
Los diagramas de barras producidos con la dos últimas instrucciones son los de la Figura 7.15.
Vemos, por ejemplo, que entre las personas de ojos azules, los cabellos rubios son los más
frecuentes, y que entre las personas castañas el color de ojos más frecuente es el pardo.
0.6
Negro Marrones
Castaño Azules
Rojo Pardos
0.5
Rubio Verdes
0.6
0.4
0.4
0.3
0.2
0.2
0.1
0.0
0.0
as.table convierte un objeto (por ejemplo, una matriz) en una tabla de contingencia.
• margin: sirve para especificar las dimensiones en cuyos niveles se calcularán las fre-
cuencias relativas marginales. Si no se especifica, se calculan las frecuencias relativas
globales.
CrossTable, del paquete gmodels, produce, en el caso bidimensional, una tabla conjunta
de frecuencias absolutas y de frecuencias relativas globales y marginales.
names da los nombres de las columnas de una tabla unidimensional, y sirve también para
modificar estos nombres.
7-30
dimnames da una list con los vectores de los nombres de los niveles de las diferentes
variables de una tabla multidimensional, y sirve también para modificar los nombres
tanto de las variables como de sus niveles.
tabla[...] se usa para especificar un elemento, una fila, una columna o una subtabla de
la tabla.
pie dibuja el diagrama circular de un vector o un factor a partir de una tabla de frecuen-
cias. Algunos parámetros importantes:
mosaic, del paquete vcd, también dibuja el diagrama de mosaico de una tabla de frecuen-
cias. Algunos parámetros importantes:
cotabplot, del paquete vcd, produce una tabla con un diagrama de mosaico para cada
nivel de la última variable.
7-31
mosaic3d, del paquete vcdExtra, produce un diagrama de mosaico tridimensional.
7.10. Ejercicio
Instalad y cargad el paquete MASS. Este paquete lleva una tabla de datos llamada birthwt
sobre factores que pueden incidir en el peso de los niños al nacer. Con str y head, explorad su
estructura, y en la pestaña de Ayuda, consultad el significado de cada variable.
(a) Calculad una tabla bidimensional de frecuencias relativas marginales de los pares (raza de
la madre, peso inferior a 2.5 kg o no) que permita ver, fácilmente, si la raza de la madre
influye en el peso del bebé. Dibujad un diagrama de mosaico de esta tabla.
Asimismo, dibujad un diagrama de barras por bloques que permita visualizar esta informa-
ción. Poned nombres adecuados a los bloques, colores a las barras, y añadid una leyenda
que explique qué representa cada barra. ¿Se puede obtener alguna conclusión de esta tabla
y de este diagrama de barras?
(b) Repetid el punto anterior para los pares (madre fumadora o no, peso inferior a 2.5 kg o no)
y para los pares (madre hipertensa o no, peso inferior a 2.5 kg o no).
(c) Calculad una tabla de frecuencias relativas marginales de las ternas (raza de la madre,
madre fumadora o no, peso inferior a 2.5 kg o no) que permita ver, fácilmente, si la combi-
nación de la raza de la madre y su condición de fumadora o no fumadora influye en el peso
del bebé. Dibujad un diagrama de mosaico de esta tabla tridimensional.
Asimismo, dibujad un diagrama de barras por bloques que permita visualizar esta informa-
ción (pensad cómo pasaréis de la tabla tridimensional a un diagrama de barras bidimensio-
nal que muestre la información deseada). Poned nombres adecuados a los bloques, colores a
las barras, y añadid una leyenda que explique qué representa cada barra. ¿Se puede obtener
alguna conclusión de esta tabla y de este diagrama de barras?
7-32
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 5
Lección 8
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 8
Descripción de datos ordinales
Los datos ordinales son parecidos a los cualitativos, en el sentido de que son cualidades de
objetos o individuos. Su diferencia con los datos cualitativos está en que las características que
expresan los datos ordinales tienen un orden natural que permite acumular observaciones, es
decir, contar cuántas hay por debajo de cada nivel. Un caso frecuente son las escalas tipo Likert,
que se usan para conocer la opinión de un grupo de personas sobre un tema determinado.1
Ejemplo 8.1. Tenemos una muestra de 20 estudiantes de quienes sabemos la calificación que
han sacado en un examen. Clasificamos estas calificaciones en Suspenso (S), Aprobado (A),
Notable (N ) y Sobresaliente (E) y consideramos su orden natural S < A < N < E. Las
calificaciones que han obtenido son las siguientes:
A, A, N, S, S, A, N, E, A, A, S, S, S, A, E, N, N, E, S, A.
En esta lista hay 6 S, 7 A, 4 N y 3 E: éstas serían las frecuencias absolutas de las calificaciones
en esta muestra de estudiantes. Por lo que se refiere a sus frecuencias absolutas acumuladas:
Hay 20 estudiantes que han obtenido E o menos (todos): la frecuencia absoluta acumulada
de E es 20.
x 1 , x2 , . . . , x n
Por lo tanto, cada una de estas observaciones xj es igual a algún li . Diremos que estas ob-
servaciones forman una variable ordinal. En el ejemplo anterior, tendríamos que los niveles
son
S < A < N < E,
que n = 20, y que x1 , . . . , x20 son las calificaciones obtenidas por los estudiantes de la muestra.
Con estas notaciones:
Las definiciones de frecuencias absolutas nj y relativas fj , para cada nivel lj , son las
mismas que en una variable cualitativa.
La frecuencia relativa acumulada del nivel lj en esta variable ordinal es la fracción (en
tanto por uno) Fj de observaciones xi tales que xi 6 lj . Es decir,
j
Nj X
Fj = = fk .
n k=1
«¿Cree que su empresa anima a sus técnicos en impacto ambiental a usar méto-
dos que favorezcan la opinión del cliente que ha encargado el estudio?»
Las posibles respuestas eran las que aparecen en la Tabla 8.1, y forman una escala ordinal de
tipo Likert, con 1 < 2 < 3 < 4 < 5.
Supongamos que se recogieron las siguientes respuestas de 100 técnicos:
4, 4, 2, 1, 3, 3, 4, 1, 1, 3, 5, 2, 5, 2, 1, 2, 3, 2, 1, 1, 2, 2, 4, 2, 1, 1, 1, 2, 4, 5, 3, 4, 2, 2,
4, 4, 3, 1, 3, 3, 2, 1, 5, 4, 1, 2, 2, 3, 3, 3, 1, 4, 3, 5, 1, 5, 1, 2, 5, 5, 2, 4, 5, 1, 4, 3, 1,
1, 4, 3, 3, 4, 4, 1, 2, 1, 3, 4, 1, 4, 2, 2, 4, 1, 3, 5, 3, 3, 3, 2, 2, 3, 3, 3, 2, 4, 1, 1, 4, 3.
8-2
Nivel Significado
1 Muy en desacuerdo
2 En desacuerdo
3 Neutro
4 De acuerdo
5 Muy de acuerdo
Tabla 8.1. Un ejemplo de datos ordinales.
Nivel n i Ni fi Fi
1 24 24 0.24 0.24
2 22 46 0.22 0.46
3 24 70 0.24 0.70
4 20 90 0.20 0.90
5 10 100 0.10 1.00
Total 100 1
Tabla 8.2. Tabla de frecuencias de los datos ordinales del Ejem-
plo 8.2.
En este caso tenemos 5 niveles (k = 5) y 100 observaciones (n = 100) que forman una variable
ordinal que llamaremos Encuesta. La Tabla 8.2 contiene todas las frecuencias, absolutas y
relativas, acumuladas o no, de esta variable ordinal. Para construirla, primero hemos calculado
las frecuencias absolutas de cada nivel (los ni ) y a partir de éstas hemos calculado las demás.
Los gráficos para frecuencias absolutas y relativas de variables ordinales son los mismos que
para variables cualitativas: diagramas de barras, diagramas circulares, etc. También podemos
utilizar diagramas de barras para describir frecuencias acumuladas: en este caso, la altura de
cada barra ha de representar la frecuencia acumulada del nivel correspondiente, y los niveles
han de aparecer ordenados, de manera que las alturas de las barras crezcan de izquierda a
derecha. No es conveniente usar diagramas circulares para representar frecuencias acumuladas,
porque no representan la información sobre la acumulación de datos de manera fácil de entender
a simple vista.
Ejemplo 8.3. Continuando con el Ejemplo 8.2, la Figura 8.1 muestra los diagramas de barras
de frecuencias relativas y relativas acumuladas de los resultados de la encuesta. En la próxima
sección explicamos cómo los hemos dibujado con R.
8-3
Diagrama de barras de las frecuencias relativas Diagrama de barras de las frecuencias relativas acumuladas
0.30 de la variable "Encuesta" de la variable "Encuesta"
1.0
0.25
0.8
0.20
0.6
Frec. relativas
Frec. relativas
acumuladas
0.15
0.4
0.10
0.2
0.05
0.00
0.0
Muy en En Neutro De Muy de Muy en En Neutro De Muy de
desacuerdo desacuerdo acuerdo acuerdo desacuerdo desacuerdo acuerdo acuerdo
> notas = ordered ( c ( " A " ," A " ," N " ," S " ," S " ," A " ," N " ," E " ," A " ," A " ," S " ," S " ,
" S " ," A " ," E " ," N " ," N " ," E " ," S " ," A " ) , levels = c ( " S " ," A " ," N " ," E " ) )
> notas
[1] A A N S S A N E A A S S S A E N N E S A
Levels : S < A < N < E
> table ( notas ) # Frec . absolutas
notas
S A N E
6 7 4 3
> cumsum ( table ( notas ) ) # Frec . absolutas acumuladas
S A N E
6 13 17 20
> cumsum ( prop . table ( table ( notas ) ) ) # Frec . relativas acumuladas
S A N E
0.30 0.65 0.85 1.00
> barplot ( table ( notas ) , main = " Diagrama de barras de frecuencias
absolutas de notas " )
> barplot ( cumsum ( table ( notas ) ) , main = " Diagrama de barras de
frecuencias acumuladas de notas " )
8-4
Diagrama de barras de frecuencias absolutas de notas Diagrama de barras de frecuencias acumuladas de notas
20
7
6
15
5
4
10
3
2
5
1
0
0
S A N E S A N E
Pensad qué ha entendido R que queríamos hacer con esta última instrucción.
Ejemplo 8.5. Los diagramas de barras mostrados en la Figura 8.1 se han obtenido con el
código siguiente.
> datos = c (4 ,4 ,2 ,1 ,3 ,3 ,4 ,1 ,1 ,3 ,5 ,2 ,5 ,2 ,1 ,2 ,3 ,2 ,1 ,1 ,2 ,2 ,4 ,2 ,1 ,1 ,1 ,
2 ,4 ,5 ,3 ,4 ,2 ,4 ,4 ,3 ,1 ,3 ,3 ,2 ,1 ,5 ,4 ,1 ,2 ,2 ,3 ,3 ,3 ,1 ,4 ,3 ,5 ,1 ,5 ,1 ,2 ,5 ,5 ,
2 ,4 ,5 ,1 ,4 ,3 ,1 ,1 ,4 ,3 ,3 ,4 ,4 ,1 ,2 ,1 ,3 ,4 ,1 ,4 ,2 ,2 ,4 ,1 ,3 ,5 ,3 ,3 ,3 ,2 ,2 ,3 ,
3 ,3 ,2 ,4 ,1 ,1 ,4 ,3 ,2)
> Nombres . compl = c ( " Muy en \ n desacuerdo " ," En \ n desacuerdo " ,
" Neutro \ n " ," De \ n acuerdo " ," Muy de \ n acuerdo " )
> barplot ( prop . table ( table ( datos ) ) , ylim = c (0 , 0.30) ,
main = " Diagrama de barras de las frecuencias relativas \ n de
la variable \ " Encuesta \ " " , names = Nombres . compl ,
xlab = " Nivel de acuerdo escala Likert " , ylab = " Frec . relativas " )
> barplot ( cumsum ( prop . table ( table ( datos ) ) ) , ylim = c (0 , 1) ,
8-5
main = " Diagrama de barras de las frecuencias relativas acumuladas
\ n de la variable \ " Encuesta \ " " , names = Nombres . compl ,
xlab = " Nivel de acuerdo escala Likert " , ylab = " Frec . relativas
acumuladas " )
En los diagramas de barras, hemos representado los niveles con un texto más descriptivo.
Para hacerlo, hemos definido un vector Nombres.compl con sus nuevos nombres en el orden
correspondiente (y en dos líneas cada uno para ocupar menos espacio horizontal), y entonces
en los barplot hemos especificado names=Nombres.compl.
Ejemplo 8.6. Un microbiólogo ha evaluado la semejanza a una cierta comunidad prototipo
de los microbiotas intestinales de 40 individuos con síndrome del colon irritable. Los niveles
de semejanza que ha usado son los de la Tabla 8.3, y los considera ordenados de la siguiente
manera:
Nivel Significado
Tot.difs Totalmente diferentes
Difs Diferentes
Pars Parecidas
Muy.pars Muy parecidas
Iguales Totalmente iguales
Tabla 8.3. Niveles de semejanza de comunidades microbianas.
Difs, Pars, Difs, Muy.pars, Muy.pars, Muy.pars, Iguales, Pars, Difs, Tot.difs,
Muy.pars, Iguales, Muy.pars, Pars, Pars, Muy.pars, Iguales, Difs, Difs, Pars,
Muy.pars, Muy.pars, Muy.pars, Difs, Pars, Pars, Iguales, Iguales, Muy.pars,
Iguales, Difs, Tot.difs, Pars, Muy.pars, Iguales, Difs, Muy.pars, Muy.pars,
Iguales, Tot.difs.
Para estudiar las frecuencias acumuladas de los niveles de semejanza en este estudio, entra-
remos los datos en un factor ordenado con los niveles ordenados de manera adecuada.
> datos = scan ( sep = " ," , what = " character " ) # Vamos a copiar del texto
y pegar los valores
1: Difs , Pars , Difs , Muy . pars , Muy . pars , Muy . pars , Iguales , Pars , Difs , Tot .
difs , Muy . pars , Iguales , Muy . pars , Pars , Pars , Muy . pars , Iguales , Difs ,
Difs , Pars , Muy . pars , Muy . pars , Muy . pars , Difs , Pars , Pars , Iguales ,
Iguales , Muy . pars , Iguales , Difs , Tot . difs , Pars , Muy . pars , Iguales ,
Difs , Muy . pars , Muy . pars , Iguales , Tot . difs
41:
Read 40 items
> sem . com = ordered ( datos , levels = c ( " Tot . difs " ," Difs " ," Pars " ," Muy .
pars " ," Iguales " ) ) # Los datos , como factor ordenado
> AbsFr = table ( sem . com ) # Ponemos nombre a la tabla de frecuencias
absolutas
8-6
> AbsFr
sem . com
Tot . difs Difs Pars Muy . pars Iguales
3 8 8 13 8
> prop . table ( AbsFr ) # Tabla de frecuencias relativas
sem . com
Tot . difs Difs Pars Muy . pars Iguales
0.075 0.200 0.200 0.325 0.200
> cumsum ( AbsFr ) # Tabla de frecuencias absolutas acumuladas
Tot . difs Difs Pars Muy . pars Iguales
3 11 19 32 40
> cumsum ( prop . table ( AbsFr ) ) # Tabla de frecuencias relativas
acumuladas
Tot . difs Difs Pars Muy . pars Iguales
0.075 0.275 0.475 0.800 1.000
> Nombres . Completos = c ( " Totalmente \ n diferentes " ," Diferentes \ n " ,
" Parecidas \ n " ," Muy \ n parecidas " ," Totalmente \ n iguales " )
> barplot ( cumsum ( prop . table ( AbsFr ) ) , names = Nombres . Completos ,
main = " Diagrama de barras de frecuencias relativas acumuladas " )
Para calcular frecuencias acumuladas en una tabla multidimensional, hay que aplicar a la
tabla la función cumsum mediante la función apply. En este caso concreto, la sintaxis de la
instrucción sería
apply(tabla, MARGIN=..., FUN=cumsum),
donde el valor de MARGIN ha de ser el de la dimensión en la que queremos acumular las frecuen-
cias: 1 si queremos calcular las frecuencias acumuladas por filas, 2 si las queremos calcular por
columnas, etc. Esta construcción tiene algunas particularidades, que vamos a ilustrar con un
ejemplo.
8-7
Ejemplo 8.7. Supongamos que las 100 respuestas a la encuesta en el Ejemplo 8.2 en realidad
provienen de técnicos de tres empresas diferentes, A, B y C, de manera que las 30 primeras son
de técnicos de A, las 20 siguientes, de técnicos de B, y las 50 últimas, de técnicos de C. Nos
interesa estudiar la distribución de las respuestas según la empresa.
Vamos a organizar estos datos en un data frame. Para que sea más fácil visualizar la infor-
mación que nos interesa, es conveniente que las filas de las tablas de frecuencias correspondan
a las empresas. Por lo tanto, al definir el data frame, entraremos como primera variable la de
las empresas; de esta manera, éstas aparecerán en las filas al aplicarle la función table.
> respuestas = ordered ( c (4 ,4 ,2 ,1 ,3 ,3 ,4 ,1 ,1 ,3 ,5 ,2 ,5 ,2 ,1 ,2 ,3 ,2 ,1 ,1 ,2 ,
2 ,4 ,2 ,1 ,1 ,1 ,2 ,4 ,5 ,3 ,4 ,2 ,4 ,4 ,3 ,1 ,3 ,3 ,2 ,1 ,5 ,4 ,1 ,2 ,2 ,3 ,3 ,3 ,1 ,4 ,3 ,5 ,
1 ,5 ,1 ,2 ,5 ,5 ,2 ,4 ,5 ,1 ,4 ,3 ,1 ,1 ,4 ,3 ,3 ,4 ,4 ,1 ,2 ,1 ,3 ,4 ,1 ,4 ,2 ,2 ,4 ,1 ,3 ,5 ,
3 ,3 ,3 ,2 ,2 ,3 ,3 ,3 ,2 ,4 ,1 ,1 ,4 ,3 ,2) , levels =1:5)
> empresas = rep ( c ( " A " ," B " ," C " ) , times = c (30 ,20 ,50) )
> df _ encuesta = data . frame ( empresas , respuestas )
> str ( df _ encuesta )
’ data . frame ’: 100 obs . of 2 variables :
$ empresas : Factor w / 3 levels " A " ," B " ," C " : 1 1 1 1 1 1 1 1 1 1
...
$ respuestas : Ord . factor w / 5 levels " 1 " < " 2 " < " 3 " < " 4 " < ..: 4 4 2 1 3
3 4 1 1 3 ...
> head ( df _ encuesta ,5)
empresas respuestas
1 A 4
2 A 4
3 A 2
4 A 1
5 A 3
> table ( df _ encuesta )
respuestas
empresas 1 2 3 4 5
A 9 9 4 5 3
B 4 4 7 4 1
C 11 9 13 11 6
Para calcular la tabla de frecuencias absolutas acumuladas de las respuestas por empresa, y
como las empresas definen las filas de la tabla anterior, hemos de usar apply con MARGIN=1.
> apply ( table ( df _ encuesta ) , MARGIN =1 , FUN = cumsum )
empresas
A B C
1 9 4 11
2 18 8 20
3 22 15 33
4 27 19 44
5 30 20 50
¡La tabla se ha traspuesto! Resulta que cuando se aplica apply a una table bidimensional,
R intercambia, si es necesario, filas por columnas en el resultado para que la dimensión de la
tabla resultante en la que se haya aplicado la función sea la de las columnas. Por lo tanto, para
volver a tener las empresas en las filas, hemos de trasponer el resultado de apply.
8-8
> t ( apply ( table ( df _ encuesta ) , MARGIN =1 , FUN = cumsum ) )
empresas 1 2 3 4 5
A 9 18 22 27 30
B 4 8 15 19 20
C 11 20 33 44 50
Vamos ahora a calcular la tabla de frecuencias relativas acumuladas de las respuestas por
empresa. Para ello, y en una sola instrucción, primero calculamos la tabla de frecuencias relativas
por filas; a continuación, las acumulamos por filas con apply y cumsum; y, finalmente, como ya
estamos avisados, trasponemos el resultado
> t ( apply ( prop . table ( table ( df _ encuesta ) , margin =1) , MARGIN =1 ,
FUN = cumsum ) )
empresas 1 2 3 4 5
A 0.30 0.6 0.7333333 0.90 1
B 0.20 0.4 0.7500000 0.95 1
C 0.22 0.4 0.6600000 0.88 1
Vamos ahora a dibujar el diagrama de barras por bloques de esta tabla. Como queremos que
las barras de este diagrama se agrupen por empresas, hemos de aplicar barplot a la tabla sin
trasponer.
> Tabla = apply ( prop . table ( table ( df _ encuesta ) , margin =1) , MARGIN =1 ,
FUN = cumsum )
> barplot ( Tabla , beside = TRUE , legend = TRUE ,
main = " Diagrama de barras de frecuencias relativas acumuladas \ n
de respuestas por empresa " )
De esta manera obtenemos el diagrama de barras de la izquierda de la Figura 8.4. Como vemos,
la leyenda se superpone sobre las barras de la última empresa. Para resolver este problema,
situaremos la leyenda en la esquina superior izquierda. Además, vamos a sustituir en la leyenda
los valores de las respuestas por sus significados, y para que quepa, reduciremos el tamaño del
texto. El resultado será el diagrama de la derecha de la Figura 8.5.
> barplot ( Tabla , beside = TRUE , legend . text = c ( " Muy en desacuerdo " ,
" En desacuerdo " ," Neutro " ," De acuerdo " ," Muy de acuerdo " ) ,
main = " Diagrama de barras de frecuencias relativas acumuladas \ n
de respuestas por empresa " ,
args . legend = list ( x = " topleft " , cex =0.55) )
Ejemplo 8.8. Consideremos el data frame InsectSprays, que viene predefinido en R. Veamos
su estructura.
> head ( InsectSprays )
count spray
1 10 A
2 7 A
3 20 A
8-9
Diagrama de barras de frecuencias relativas acumuladas Diagrama de barras de frecuencias relativas acumuladas
1.0 de respuestas por empresa de respuestas por empresa
1.0
Muy en desacuerdo
En desacuerdo
1 Neutro
De acuerdo
2 Muy de acuerdo
3
4
0.8
0.8
5
0.6
0.6
0.4
0.4
0.2
0.2
0.0
0.0
A B C A B C
4 14 A
5 14 A
6 12 A
> str ( InsectSprays )
’ data . frame ’: 72 obs . of 2 variables :
$ count : num 10 7 20 14 14 12 10 23 17 20 ...
$ spray : Factor w / 6 levels " A " ," B " ," C " ," D " ,..: 1 1 1 1 1 1 1 1 1
1 ...
> table ( InsectSprays $ count )
0 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16 17 19 20 21
2 6 4 8 4 7 3 3 1 3 3 2 4 4 2 2 4 1 2 2
22 23 24 26
1 1 1 2
> help ( InsectSprays )
Con el help(InsectSprays) nos enteramos de que la variable numérica count contiene los
números de insectos que se encontraron en diferentes superficies agrícolas, todas de la misma
área, tratadas con el insecticida indicado por el factor spray.
Vamos a convertir la variable count en una variable ordinal que agrupe las entradas de la
variable original en niveles
«Entre 0 y 4», «Entre 5 y 9», «Entre 10 y 14», «Entre 15 y 19» y «20 o más».
La manera más sencilla de llevarlo a cabo es usando la función cut, que estudiaremos en detalle
en la Lección 11. Por ahora es suficiente saber que la instrucción
> cut ( InsectSprays $ count , breaks = c (0 ,5 ,10 ,15 ,20 , Inf ) , right = FALSE ,
labels = c ( " 0 - 4 " ," 5 - 9 " ," 10 - 14 " ," 15 - 19 " ," 20 - ... " ) )
8-10
de corte pertenecen al intervalo a su derecha, e Inf indica 1. Por tanto, los intervalos en los
que esta instrucción agrupa los números de insectos son
[0, 5), [5, 10), [10, 15), [15, 20), [20, 1).
El resultado de la instrucción es un factor que tiene como niveles estos intervalos, identificados
con las etiquetas especificadas en el parámetro labels. Como nosotros vamos a usar estos
intervalos como niveles de una variable ordinal, además convertiremos este factor en ordenado.
Los cambios lo realizaremos en una copia de InsectSprays.
> IS = InsectSprays
> IS $ count . rank = ordered ( cut ( IS $ count , breaks = c (0 ,5 ,10 ,15 ,20 , Inf ) ,
right = FALSE , labels = c ( " 0 - 4 " ," 5 - 9 " ," 10 - 14 " ," 15 - 19 " ," 20 - ... " ) ) )
> str ( IS )
’ data . frame ’: 72 obs . of 3 variables :
$ count : num 10 7 20 14 14 12 10 23 17 20 ...
$ spray : Factor w / 6 levels " A " ," B " ," C " ," D " ,..: 1 1 1 1 1 1 1
1 1 1 ...
$ count . rank : Ord . factor w / 5 levels " 0 - 4 " < " 5 - 9 " < " 10 - 14 " < ..: 3 2 5
3 3 3 3 5 4 5 ...
Nos interesa estudiar la distribución de los números de insectos según el tipo de insecticida.
Por lo tanto, vamos a calcular las tablas bidimensionales de frecuencias relativas y relativas acu-
muladas de los intervalos de números de insectos en cada nivel de Spray, y las representaremos
por medio de diagramas de barras.
La tabla de frecuencias absolutas de los pares (insecticida, intervalo de números de insectos)
se puede obtener aplicando table al data frame formado por las dos últimas columnas de IS.
> Tab = table ( IS [ ,2:3])
> Tab
count . rank
spray 0 - 4 5 - 9 10 - 14 15 - 19 20 - ...
A 0 1 7 1 3
B 0 1 4 5 2
C 11 1 0 0 0
D 5 6 1 0 0
E 8 4 0 0 0
F 0 1 4 3 4
> Freq . rel = round ( prop . table ( Tab , margin =1) , 3) # Frecuencias
relativas por Spray
> Freq . rel
count . rank
spray 0-4 5 - 9 10 - 14 15 - 19 20 - ...
A 0.000 0.083 0.583 0.083 0.250
B 0.000 0.083 0.333 0.417 0.167
C 0.917 0.083 0.000 0.000 0.000
D 0.417 0.500 0.083 0.000 0.000
E 0.667 0.333 0.000 0.000 0.000
F 0.000 0.083 0.333 0.250 0.333
> Freq . rel . acum = round ( apply ( prop . table ( Tab , margin =1) , MARGIN =1 ,
FUN = cumsum ) , 3) # Frecuencias relativas acumuladas
8-11
> Freq . rel . acum
spray
A B C D E F
0-4 0.000 0.000 0.917 0.417 0.667 0.000
5-9 0.083 0.083 1.000 0.917 1.000 0.083
10 - 14 0.667 0.417 1.000 1.000 1.000 0.417
15 - 19 0.750 0.833 1.000 1.000 1.000 0.667
20 - ... 1.000 1.000 1.000 1.000 1.000 1.000
> t ( Freq . rel . acum )
Las dos últimas instrucciones producen los diagramas de barras de la Figura 8.5. Observamos
que los insecticidas C, D y E son los más efectivos, porque producen mayores números de
campos con pocos insectos, mientras que B y F son poco efectivos.
1.0
0-4
0-4 5-9
5-9 10-14
15-19
10-14
20-...
15-19
0.8
0.8
20-...
0.6
0.6
0.4
0.4
0.2
0.2
0.0
0.0
A B C D E F A B C D E F
8-12
8.3. Guía rápida de funciones
ordered sirve para construir factores ordenados. Sus parámetros principales se pueden
consultar en la guía de la Lección 3.
table calcula la tabla de frecuencias absolutas de un vector o un factor.
t sirve para trasponer una tabla bidimensional.
prop.table calcula la tabla de frecuencias relativas de un vector o un factor a partir de
su tabla de frecuencias absolutas. Sus parámetros principales se pueden consultar en la
guía de la Lección 7.
cumsum calcula las sumas acumuladas de un vector.
apply(tabla, MARGIN=..., FUN=función) aplica la función a los niveles de las variables
de la tabla que se especifican en MARGIN. Para calcular frecuencias acumuladas en tablas
multidimensionales, se usa FUN=cumsum.
barplot dibuja el diagrama de barras de un vector o un factor a partir de una tabla de
frecuencias. Sus parámetros principales se pueden consultar en la guía de la Lección 7.
8.4. Ejercicio
La tabla de datos http://aprender.uib.es/Rdir/Notas2011A.txt contiene las notas obte-
nidas por los estudiantes de los grados de Biología y Bioquímica de la UIB en un examen de
la asignatura Matemáticas I del curso 2011-12 (no aparecen los no presentados, por lo que no
hay valores NA). Las variables que contiene la tabla para cada estudiante son: su nota numéri-
ca (sobre 10), su calificación alfabética (S significa Suspenso, A, Aprobado, N, Notable, y E,
Sobresaliente) y su grupo (BLM para el grupo de las mañanas de Biología, BLT para el grupo
de las tardes de Biología, y BQ para Bioquímica).
(a) Definid un data frame con esta tabla, de manera que su variable NotasLetra sea un factor
ordenado, con las calificaciones alfabéticas ordenadas según su orden natural S < A < N <
E. Comprobad con str y head que el data frame obtenido tiene la estructura deseada.
(b) Comprobad que las notas alfabéticas se corresponden con las nota numéricas: [0, 5[ co-
rresponde a S; [5, 7[, a A; [7, 9[, a N; y [9, 10], a E. Si hay algún error, corregid la nota
alfabética.
(c) Calculad la tabla bidimensional de frecuencias relativas de las calificaciones alfabéticas por
grupo, con las frecuencias relativas calculadas dentro de los grupos. Redondead a 2 cifras
decimales. Los grupos tienen que definir las filas, y las notas, las columnas.
(d) Calculad la tabla bidimensional de frecuencias relativas acumuladas de las calificaciones
alfabéticas por grupo, con las frecuencias relativas calculadas dentro de los grupos. Redon-
dead a 2 cifras decimales. Los grupos tienen que definir las filas, y las notas, las columnas.
(e) Dibujad un diagrama de barras por bloques de las frecuencias relativas de las calificaciones
alfabéticas dentro de cada grupo: los bloques han de corresponder a los grupos. Poned un
título al gráfico y añadid una leyenda que explique cada barra qué calificación representa.
Podéis poner colores, cambiar los nombres, etc. para mejorar su aspecto.
8-13
(f) Dibujad un diagrama de barras por bloques de las frecuencias relativas acumuladas de las
calificaciones alfabéticas dentro de cada grupo: los bloques han de corresponder a los grupos.
Como antes, poned un título al gráfico y añadid una leyenda que explique cada barra qué
calificación representa. Podéis poner colores, cambiar los nombres, etc. para mejorar su
aspecto.
(g) ¿Podéis extraer alguna conclusión de las tablas y los gráficos anteriores?
8-14
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 6
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 9
Descripción de datos cuantitativos
Los datos cuantitativos son los que expresan cantidades que se representan mediante números,
tales como los resultados de contar objetos o individuos o de medir pesos, distancias, tiempos
o concentraciones, y se suelen clasificar en continuos y discretos. Los datos continuos son los
que, si pudiéramos medirlos con precisión infinita, en principio podrían tomar todos los valores
de un intervalo de la recta real: por ejemplo, el peso o la altura de un individuo o el tiempo que
tarda un determinado proceso. En cambio, los datos discretos son los que pueden tomar sólo un
conjunto contable de valores: el resultado obtenido al lanzar un dado, el número de individuos
en una población, el número de aminoácidos en una proteína. . . En todo caso, hay que tener en
cuenta que esta distinción es sólo teórica: en la práctica, todos los datos son discretos, ya que
la precisión infinita no existe. Pero a veces es necesario suponer que unos datos son continuos
para poder usar técnicas específicas en su análisis.
Para estudiar una variable cuantitativa (una lista de datos cuantitativos), podemos usar las
frecuencias y las frecuencias acumuladas de sus diferentes valores, como en las variables ordi-
nales, puesto que podemos ordenar los datos cuantitativos con el orden natural de los números
reales. Pero además de las frecuencias, disponemos de otras muchas técnicas descriptivas, ya
que, como los datos cuantitativos son números reales y tienen el significado de números reales,
podemos operar con ellos.
Los datos cuantitativos admiten dos tipos de tratamiento, según trabajemos con los datos
originales o brutos (raw data) o los agrupemos en clases o intervalos (recordad el Ejemplo 8.8).
En esta lección vamos a tratar sólo la primera situación, y en la Lección 11 estudiaremos la
descripción de datos agrupados.
9.1. Frecuencias
El tratamiento de las frecuencias de datos cuantitativos es similar al de los datos ordinales,
excepto por el hecho de que no se tienen en cuenta todos los niveles posibles, sino sólo los
observados.
Ejemplo 9.1. Hemos pedido las edades a un grupo de 15 voluntarios de una ONG. Las res-
puestas, en años, han sido las siguientes:
18, 22, 16, 19, 23, 18, 35, 16, 45, 20, 20, 22, 40, 18, 45.
Las diferentes edades que hemos observado son 16, 18, 19, 20, 22, 23, 35, 40 y 45, y por lo tanto
sólo nos interesan las frecuencias de estas edades. Las podemos calcular con R y así, de paso,
recordaremos cómo se hace.
> edades = c (18 ,22 ,16 ,19 ,23 ,18 ,35 ,16 ,45 ,20 ,20 ,22 ,40 ,18 ,45)
> table ( edades ) # Frecuencias absolutas
edades
16 18 19 20 22 23 35 40 45
2 3 1 2 2 1 1 1 2
> round ( prop . table ( table ( edades ) ) , 2) # Frecuencias relativas
edades
16 18 19 20 22 23 35 40 45
0.13 0.20 0.07 0.13 0.13 0.07 0.07 0.07 0.13
> cumsum ( table ( edades ) ) # Frecuencias absolutas acumuladas
16 18 19 20 22 23 35 40 45
2 5 6 8 10 11 12 13 15
> round ( cumsum ( prop . table ( table ( edades ) ) ) , 2) # Frecuencias
relativas acumuladas
16 18 19 20 22 23 35 40 45
0.13 0.33 0.40 0.53 0.67 0.73 0.80 0.87 1.00
Supongamos que realizamos n observaciones de una propiedad que se mide con un número
real, y obtenemos la lista de datos cuantitativos (la variable cuantitativa)
x1 , . . . , x n .
Sean X1 , . . . , Xk los valores distintos que aparecen en esta lista de datos; los consideraremos
ordenados
X 1 < X2 < · · · < Xk .
Entonces, en esta variable cuantitativa:
nj
La frecuencia relativa de Xj es fj = .
n
j
Nj X
La frecuencia relativa acumulada de Xj es Fj = = fi .
n i=1
Ejemplo 9.2. Lanzamos 10 veces un dado de seis caras al aire y anotamos los resultados:
1, 2, 1, 4, 5, 6, 3, 5, 6, 3.
X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6.
Vamos a calcular las frecuencias de este experimento, y las organizaremos en forma de data
frame para visualizarlas como una tabla:
> dados = c (1 ,2 ,1 ,4 ,5 ,6 ,3 ,5 ,6 ,3)
> table ( dados )
dados
1 2 3 4 5 6
2 1 2 1 2 2
> prop . table ( table ( dados ) )
9-2
dados
1 2 3 4 5 6
0.2 0.1 0.2 0.1 0.2 0.2
> cumsum ( table ( dados ) )
1 2 3 4 5 6
2 3 5 6 8 10
> cumsum ( prop . table ( table ( dados ) ) )
1 2 3 4 5 6
0.2 0.3 0.5 0.6 0.8 1.0
> tabla _ df = data . frame ( Resultado =1:6 ,
Frec _ Abs = as . vector ( table ( dados ) ) ,
Frec _ Rel = as . vector ( round ( prop . table ( table ( dados ) ) , 2) ) ,
Frec _ Abs _ Acum = as . vector ( cumsum ( table ( dados ) ) ) ,
Frec _ Rel _ Acum = as . vector ( round ( cumsum ( prop . table ( table ( dados ) ) ) ,
2) ) )
> tabla _ df
Resultado Frec _ Abs Frec _ Rel Frec _ Abs _ Acum Frec _ Rel _ Acum
1 1 2 0.2 2 0.2
2 2 1 0.1 3 0.3
3 3 2 0.2 5 0.5
4 4 1 0.1 6 0.6
5 5 2 0.2 8 0.8
6 6 2 0.2 10 1.0
Para entrar una tabla unidimensional como una variable en un data frame, es conveniente trans-
formarla en vector con as.vector. De lo contrario, cada table y cada prop.table añadirían
una columna extra con los nombres de los niveles. Comprobadlo.
La moda, que es el valor, o los valores, de máxima frecuencia (absoluta o relativa, tanto
da).
La media aritmética, o valor medio,
Pn Pk k
j=1 nj · Xj
X
i=1 xi
x= = = fj · Xj .
n n j=1
9-3
• Si n es impar, el dato central: x( n+1 ) .
2
En estos apuntes, cuando hablemos de la media de unos datos nos referiremos siempre a su media
aritmética. Hay otros tipos de media, como por ejemplo la media geométrica o la armónica,
que no estudiaremos.
16, 16, 18, 18, 18, 19, 20, 20, 22, 22, 23, 35, 40, 45, 45
1, 1, 2, 3, 3, 4, 5, 5, 6, 6.
9-4
9.3. Medidas de posición
Las medidas de posición estiman qué valores dividen la población en unas determinadas pro-
porciones; los valores que determinan estas posiciones reciben el nombre de cuantiles. En este
sentido, la mediana es también una medida de posición, puesto que divide la variable en dos
mitades.
Dada una proporción 0 < p < 1, el cuantil de orden p de una variable cuantitativa, que
denotaremos por Qp , es el valor más pequeño tal que su frecuencia relativa acumulada es mayor
o igual que p. En otras palabras, si tenemos un conjunto de datos x1 , . . . , xn y los ordenamos
de menor a mayor, Qp es el número más pequeño que deja a su izquierda (incluyéndolo a él)
como mínimo la fracción p de los datos, es decir, p · n datos. De esta manera, la mediana viene
a ser el cuantil Q0.5 .
Ejemplo 9.4. Consideremos otro experimento de lanzamiento de dados. Esta vez lo lanzamos
30 veces y obtenemos los resultados siguientes:
2, 4, 5, 6, 3, 2, 4, 5, 1, 2, 1, 3, 4, 2, 3, 4, 1, 2, 5, 6, 5, 5, 3, 2, 1, 3, 4, 2, 2, 1.
Si nos pidieran el cuantil Q0.2 , sería el primer elemento en esta lista ordenada que fuera mayor o
igual que, como mínimo, el 20 % de los datos. Como el 20 % de 30 es 6, sería el sexto elemento.
> dados2 [6]
[1] 2
Si nos pidieran en cambio Q0.65 , sería el primer elemento en esta lista ordenada mayor o igual
que, como mínimo, el 65 % de los datos. Como el 65 % de 30 es 19.5, sería el vigésimo elemento.
> dados2 [20]
[1] 4
También podemos calcular los cuantiles comparando la proporción p con las frecuencias re-
lativas acumuladas.
> cumsum ( prop . table ( table ( dados2 ) ) )
1 2 3 4 5 6
0.1666667 0.4333333 0.6000000 0.7666667 0.9333333 1.0000000
El primer elemento con frecuencia relativa acumulada > 0.2 es 2, lo que implica que Q0.2 = 2,
y el primer elemento con frecuencia relativa acumulada > 0.65 es 4, por lo que Q0.65 = 4.
9-5
La mediana es el cuantil Q0.5 .
Los cuartiles son los cuantiles Q0.25 , Q0.5 y Q0.75 , y reciben, respectivamente, los nombres
de primer cuartil, segundo cuartil (o mediana) y tercer cuartil. Q0.25 será, pues, el menor
valor que es mayor o igual que una cuarta parte de los datos, y Q0.75 , el menor valor que
es mayor o igual que tres cuartas partes de los datos.
Los deciles son los cuantiles Qp con p un múltiplo entero de 0.1: el primer decil es Q0.1 ,
el segundo decil es Q0.2 , y así sucesivamente.
Ha llegado el momento de avisar que la definición de cuantil que hemos dado es más bien
orientativa; en realidad, y salvo para el caso de la mediana, no hay un consenso sobre cómo
se tienen que calcular los cuantiles, de manera que se han propuesto métodos diferentes que
pueden dar resultados diferentes. La razón es que el objetivo final del cálculo de cuantiles no
es solo encontrar el primer valor cuya frecuencia relativa acumulada en la variable sea mayor o
igual que p, sino también estimar qué vale este valor para el total de la población.
Con R, los cuantiles de orden p de un vector x se calculan con la instrucción
quantile(x, p).
Veamos algunos ejemplos:
> x = c (1 ,2 ,3 ,4 ,5 ,6 ,2 ,3 ,2 ,3 ,4 ,2 ,2 ,3 ,2 ,2 ,5 ,7 ,3 ,4 ,2 ,1 ,3 ,6)
> round ( cumsum ( prop . table ( table ( x ) ) ) , 3)
1 2 3 4 5 6 7
0.083 0.417 0.667 0.792 0.875 0.958 1.000
> quantile (x , 0.1)
10 %
2
> quantile (x , 0.25)
25 %
2
> quantile (x , 0.75)
75 %
4
R dispone de 9 métodos diferentes para calcular cuantiles, que se pueden especificar dentro de
quantile con el parámetro type. En la mayoría de las ocasiones se obtiene el mismo resultado
con todos los métodos, pero no siempre. Para saber en detalle las fórmulas que usa quantile
para cada valor de type, se puede consultar la entrada correspondiente de la Wikipedia.1 El
método que hemos usado en el Ejemplo 9.4 es el que corresponde a type=1, y siempre da un
dato de los observados. El problema es que, entonces, quantile(x,0.5,type=1) y median(x)
pueden dar resultados diferentes. El método por defecto, que no hace falta especificar, es type=7.
> x = c (1 ,2 ,3 ,4 ,5 ,6 ,2 ,3 ,2 ,3 ,4 ,2 ,2 ,3 ,2 ,2 ,5 ,7 ,3 ,4 ,2 ,1 ,3 ,6)
> round ( cumsum ( prop . table ( table ( x ) ) ) , 3)
1 2 3 4 5 6 7
1
http://en.wikipedia.org/wiki/quantile
9-6
0.083 0.417 0.667 0.792 0.875 0.958 1.000
> quantile (x , 0.67)
67 %
3.41
> quantile (x , 0.67 , type =1)
67 %
4
> dados = c (1 ,2 ,1 ,4 ,5 ,6 ,3 ,5 ,6 ,3)
> round ( cumsum ( prop . table ( table ( dados ) ) ) , 3)
1 2 3 4 5 6
0.2 0.3 0.5 0.6 0.8 1.0
> median ( dados )
[1] 3.5
> quantile ( dados , 0.5 , type =1)
50 %
3
> quantile ( dados , 0.5 , type =7)
50 %
3.5
La varianza, que es la media aritmética de las diferencias al cuadrado entre los datos xi
y la media x de la variable; la denotamos por s2 . Es decir,
Pn Pk k
X
2 i=1 (xi x)2 ni · (Xi x)2
s = = i=1
= fi · (Xi x)2 .
n n i=1
p
La desviación típica, que es la raíz cuadrada positiva s de la varianza: s = s2 .
9-7
La desviación
p típica muestral , que es la raíz cuadrada positiva s̃ de la varianza muestral:
s̃ = s̃ .
2
Si s2 = 0, todos los sumandos (xi x)2 son 0 y, por lo tanto, todos los datos son iguales
a su media. En particular, s2 = 0 significa que todos los datos son iguales.
9-8
de donde deducimos que
Pn Pn
2 x2i nx2 x2i
s = i=1
= i=1
x2 .
n n
Es decir, la varianza es la media de los cuadrados de los datos, menos el cuadrado de la
media de los datos.
Hay que ir con cuidado con la desviación típica y la desviación típica muestral. En los trabajos
científicos es frecuente que se utilice una u otra sin especificar cuál es, y se la llame «desviación
típica» (standard deviation) y se la denote por s independientemente de cuál sea en realidad.
Asimismo, la mayoría de paquetes estadísticos llevan funciones para calcular la varianza y la
desviación típica (sin más aclaraciones) que, en realidad, calculan sus versiones muestrales;
como veremos en un momento, éste va ser justamente el caso de R. El motivo es que, como ya
hemos comentado, suele interesar más su aspecto inferencial que el descriptivo.
Con R, podemos calcular la medidas de dispersión para un vector x definidas al principio de
esta sección mediante de las funciones siguientes:
Para calcular su varianza, tenemos que multiplicar el resultado de var por (n 1)/n,
donde n es el número de datos que contiene x (que podemos calcular con length); por
consiguiente, la varianza de x se puede calcular con la instrucción
var(x)*(length(x)-1)/length(x).
Para calcular su desviación típica, tenemos que efectuar la raíz cuadrada de la varianza,
calculada con el procedimiento anterior: por ejemplo (y aprovechando que, por definición,
sd(...)=sqrt(var(...))), mediante la instrucción
sd(x)*sqrt((length(x)-1)/length(x)).
¡Id con cuidado! Recordad que las funciones var y sd no calculan la varianza y la desviación
típica, sino sus versiones muestrales.
> x = c (1 ,2 ,3 ,4 ,5 ,6 ,2 ,3 ,2 ,3 ,4 ,2 ,2 ,3 ,2 ,2 ,5 ,7 ,3 ,4 ,2 ,1 ,3 ,6)
> diff ( range ( x ) ) # Rango
[1] 6
> IQR ( x ) # Rango intercuart í lico
[1] 2
> var ( x ) # Varianza muestral
[1] 2.606884
> sd ( x ) # Desviaci ó n t í pica muestral
9-9
[1] 1.614585
> var ( x ) * ( length ( x ) - 1) / length ( x ) # Varianza
[1] 2.498264
> sd ( x ) * sqrt (( length ( x ) - 1) / length ( x ) ) # Desviaci ó n t í pica
[1] 1.580590
> sqrt ( var ( x ) * ( length ( x ) - 1) / length ( x ) ) # Otra manera de calcular la
desviaci ó n t í pica
[1] 1.580590
La función summary produce otros tipos de resúmenes para otras clases de objetos; por ejemplo,
ya vimos en la Lección 2 el resultado de aplicar summary a una lm.
Cuando aplicamos la función summary a un data frame, se aplica simultáneamente a todas
sus variables, y así de manera rápida podemos observar si hay diferencias apreciables entre sus
variables numéricas. A modo de ejemplo, si la aplicamos al data frame formado por las variables
numéricas de la tabla iris, obtenemos lo siguiente:
> summary ( iris [ ,1:4])
Sepal . Length Sepal . Width Petal . Length Petal . Width
Min . :4.300 Min . :2.000 Min . :1.000 Min . :0.100
1 st Qu .:5.100 1 st Qu .:2.800 1 st Qu .:1.600 1 st Qu .:0.300
Median :5.800 Median :3.000 Median :4.350 Median :1.300
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3 rd Qu .:6.400 3 rd Qu .:3.300 3 rd Qu .:5.100 3 rd Qu .:1.800
Max . :7.900 Max . :4.400 Max . :6.900 Max . :2.500
9-10
Mean :6.588 Mean :5.552
3 rd Qu .:6.900 3 rd Qu .:5.875
Max . :7.900 Max . :6.900
y deducimos así a simple vista que los pétalos y sépalos de las iris virgínica son más grandes
que los de las iris setosa.
La función by sirve para aplicar una función a algunas columnas de un data frame segmen-
tándolas según los niveles de un factor. Su sintaxis es
by(columnas, factor , FUN=función)
Por lo tanto, usando by con FUN=summary, podemos calcular este resumen estadístico en las
subpoblaciones definidas por los niveles de un factor. Por ejemplo:
> by ( iris [ , 1:4] , iris $ Species , FUN = summary )
iris $ Species : setosa
Sepal . Length Sepal . Width Petal . Length Petal . Width
Min . :4.300 Min . :2.300 Min . :1.000 Min . :0.100
1 st Qu .:4.800 1 st Qu .:3.200 1 st Qu .:1.400 1 st Qu .:0.200
Median :5.000 Median :3.400 Median :1.500 Median :0.200
Mean :5.006 Mean :3.428 Mean :1.462 Mean :0.246
3 rd Qu .:5.200 3 rd Qu .:3.675 3 rd Qu .:1.575 3 rd Qu .:0.300
Max . :5.800 Max . :4.400 Max . :1.900 Max . :0.600
---------------------------------------------------
iris $ Species : versicolor
Sepal . Length Sepal . Width Petal . Length Petal . Width
Min . :4.900 Min . :2.000 Min . :3.00 Min . :1.000
1 st Qu .:5.600 1 st Qu .:2.525 1 st Qu .:4.00 1 st Qu .:1.200
Median :5.900 Median :2.800 Median :4.35 Median :1.300
Mean :5.936 Mean :2.770 Mean :4.26 Mean :1.326
3 rd Qu .:6.300 3 rd Qu .:3.000 3 rd Qu .:4.60 3 rd Qu .:1.500
Max . :7.000 Max . :3.400 Max . :5.10 Max . :1.800
---------------------------------------------------
iris $ Species : virginica
Sepal . Length Sepal . Width Petal . Length Petal . Width
Min . :4.900 Min . :2.200 Min . :4.500 Min . :1.400
1 st Qu .:6.225 1 st Qu .:2.800 1 st Qu .:5.100 1 st Qu .:1.800
Median :6.500 Median :3.000 Median :5.550 Median :2.000
Mean :6.588 Mean :2.974 Mean :5.552 Mean :2.026
3 rd Qu .:6.900 3 rd Qu .:3.175 3 rd Qu .:5.875 3 rd Qu .:2.300
Max . :7.900 Max . :3.800 Max . :6.900 Max . :2.500
La mayoría de las instrucciones para calcular medidas estadísticas no admiten valores NA.
> z = c (1 ,2 , NA ,4)
> sum ( z )
9-11
[1] NA
> mean ( z )
[1] NA
> var ( z )
[1] NA
Los lados inferior y superior de la caja representan el primer y el tercer cuartil, por lo
que la altura de la caja es igual al rango intercuartílico.
La línea gruesa que divide la caja marca la mediana.
Los valores binf , bsup son los bigotes (whiskers) del gráfico. Si denotamos por m y M el
mínimo y el máximo de los datos, estos valores se calculan con las fórmulas
binf = máx{m, Q0.25 1.5 · (Q0.75 Q0.25 )}
bsup = mín{M, Q0.75 + 1.5 · (Q0.75 Q0.25 )}
Es decir, los bigotes marcan el mínimo y el máximo de la variable, excepto cuando están
muy alejados de la caja intercuartílica; en este caso, el bigote inferior marca el valor por
debajo de Q0.25 a distancia 1.5 veces la altura de esta caja, y el superior marca el valor
por encima de Q0.75 a distancia 1.5 veces la altura de esta caja.
Si hay datos más allá de los bigotes (menores que binf o mayores que bsup ), se marcan
como puntos aislados: son los valores atípicos (outliers) de la variable.
La instrucción básica para dibujar un diagrama de caja con R es boxplot. Por ejemplo,
> x = c (1 ,2 ,3 ,4 ,5 ,6 ,2 ,3 ,2 ,3 ,4 ,2 ,2 ,3 ,2 ,2 ,5 ,7 ,3 ,4 ,2 ,1 ,3 ,6)
> boxplot ( x )
9-12
10
● valores atípicos
b sup
5
Q 0.75
Q 0.5
Q 0.25
b
0
inf
−5
● valores atípicos
> boxplot ( iris [ iris $ Species == " setosa " , ] $ Petal . Length , main = "
Longitudes de p é talos de las Iris setosa " , yaxp = c (1 ,1.9 ,9) )
produce el diagrama de la izquierda de la Figura 9.4. Hemos especificado dentro del boxplot las
etiquetas de los diagramas de caja con el parámetro names: de lo contrario, el gráfico hubiera
9-13
Longitudes de pétalos de las Iris setosa
1.9
1.8
1.7
1.6
1.5
1.4
1.3
1.2
1.1
1.0
sido más difícil de interpretar, probadlo. Si preferís las cajas horizontales, podéis añadir el
parámetro horizontal=TRUE:
> boxplot (x , y , z , names = c ( " x " ," y " ," z " ) , horizontal = TRUE )
z
5
4
y
3
2
x
1
x y z 1 2 3 4 5 6 7
produce el gráfico de la izquierda de la Figura 9.5. Observaréis que este gráfico no es muy
satisfactorio. Dibujar el diagrama de caja de la variable «Species», que es un factor, no tiene
ningún sentido, y los nombres, además, no han quedado muy vistosos; podemos mejorar este
9-14
gráfico, incluyendo sólo las cuatro primeras variables y cambiando un poco los nombres, con la
instrucción siguiente, que produce el gráfico de la derecha de la Figura 9.5:
> boxplot ( iris [ , 1:4] , names = c ( " Sepal \ n length " , " Sepal \ n width " ,
" Petal \ n length " , " Petal \ n width " ) )
8
6
4
2
0
El objetivo de agrupar varios diagramas de caja en un único gráfico suele ser el poder compa-
rarlos visualmente, y esto normalmente sólo tiene sentido cuando las variables tienen significados
muy similares, o mejor, cuando son la misma variable sobre poblaciones diferentes. En concreto,
a menudo querremos producir diagramas de caja de una variable cuantitativa segmentada por
un factor, porque esto nos permitirá comparar el comportamiento de esta variable sobre cada
uno de los niveles del factor. La manera más conveniente de hacerlo es partir de un data frame
donde el factor sea una variable, digamos, F , y el vector de datos numéricos otra variable,
digamos, X. Entonces, para cada nivel L de F , obtendremos un diagrama de caja de los valores
que toma la variable X en los individuos de nivel L.
La sintaxis básica de la instrucción para dibujar en un único gráfico los diagramas de caja de
una variable numérica de un data frame segmentada por un factor del data frame es
boxplot(variable numérica˜variable factor , data=data frame).
A modo de ejemplo, para dibujar en un único gráfico un diagrama de caja de la variable
«Sepal.Length» del data frame iris para cada uno de los niveles del factor «Species», podemos
entrar
> boxplot ( Sepal . Length ~ Species , data = iris , ylab = " Longitudes de
s é palos ( cm ) " , main = " Tabla Iris " )
y obtenemos la Figura 9.6, donde podemos observar diferencias sustanciales entre las longitudes
de los sépalos de las tres especies, y además un valor inusualmente pequeño en el conjunto de
valores de las flores virgínica.
Aparte de los parámetros de la función plot que tengan sentido, la función boxplot dispone
de algunos parámetros específicos. Destacamos el parámetro notch que, igualado a TRUE, añade
9-15
Tabla Iris
8.0
7.5
7.0
Longitudes de sépalos (cm)
6.5
6.0
5.5
5.0
●
4.5
una muesca en la mediana de cada caja. Estas muescas se calculan de tal manera que si las de
dos diagramas de caja no se solapan, se puede tomar como evidencia significativa de que las
medianas de las poblaciones correspondientes son diferentes; por ejemplo,
> boxplot ( Sepal . Length ~ Species , data = iris , main = " Tabla Iris " ,
notch = TRUE , ylab = " Longitudes de s é palos ( cm ) " ,
col = c ( " red " ," blue " ," green " ) )
produce el gráfico de la Figura 9.7 donde, colores aparte, vemos que las muescas no se solapan,
lo que nos permite afirmar con un alto grado de confianza que las medianas de las longitudes
de los sépalos de las tres especies de flores iris son diferentes (en general, y no sólo para la
muestra concreta recogida en el data frame). Este tipo de conclusiones son las que persigue la
estadística inferencial.2
A veces es útil superponer a un diagrama de caja una marca en el valor correspondiente a la
media aritmética. Para ello se puede usar la función points. Entrad las instrucciones siguientes:
> boxplot ( Sepal . Length ~ Species , data = iris , col = " lightgray " )
> medias = aggregate ( Sepal . Length ~ Species , data = iris , FUN = mean )
> points ( medias , col = " red " , pch =18)
La primera instrucción produce el diagrama de caja de las longitudes de los sépalos de las flores
iris agrupadas según la especie, de color gris claro; la segunda, calcula las medias de dichas
longitudes para cada especie; finalmente, la tercera, añade al diagrama de caja de cada especie
un diamante rojo en la ordenada correspondiente al valor de su media. El resultado es la Figura
9.8.
El resultado de una instrucción boxplot tiene una estructura interna que podemos aprove-
char. Observemos, por ejemplo, el resultado siguiente:
2
Pero cuidado, que podamos afirmar algo sobre el total de la población con un alto grado de confianza no
significa que sea verdad.
9-16
Tabla Iris
8.0
7.5
7.0
Longitudes de sépalos (cm)
6.5
6.0
5.5
5.0
4.5
Esto muestra que un boxplot, como objeto de R, es en realidad una list. Los significados de
sus componentes se pueden consultar en la Ayuda de la función. Aquí queremos destacar las
siguientes:
stats nos da, para cada diagrama de caja en el gráfico, las ordenadas de sus cinco líneas
horizontales: binf , Q0.25 , Q0.5 , Q0.75 , bsup .
> # A ñ adimos el par á metro plot = FALSE para que no dibuje el boxplot
> boxplot ( Sepal . Length ~ Species , data = iris , plot = FALSE ) $ stats
[ , 1] [ , 2] [ , 3]
[1 , ] 4.3 4.9 5.6
[2 , ] 4.8 5.6 6.2
[3 , ] 5.0 5.9 6.5
[4 , ] 5.2 6.3 6.9
[5 , ] 5.8 7.0 7.9
out nos da los valores atípicos. Si hay más de un diagrama de caja, la componente group
nos indica los diagramas a los que pertenecen estos valores atípicos.
> boxplot ( Sepal . Length ~ Species , data = iris , plot = FALSE ) $ out
[1] 4.9
> boxplot ( Sepal . Length ~ Species , data = iris , plot = FALSE ) $ group
9-17
8.0
7.5
7.0
6.5
6.0
5.5
5.0
4.5
[1] 3
Ejemplo 9.5. Recordemos del Ejemplo 8.8 el data frame InsectSprays que viene predefinido
en R. Este data frame tiene una variable factor spray con 6 niveles que corresponden a tipos
de insecticidas, y una variable numérica count que contiene números de insectos recolectados
en campos tratados con los insecticidas. En aquel ejemplo convertimos la variable count en
una variable ordinal, pero está claro que se trata de una variable cuantitativa; por lo tanto,
podemos usar las técnicas explicadas en esta lección para comparar de manera más significativa
la efectividad de los insecticidas a partir de los datos brutos de esta variable.
En primer lugar, obtenemos un resumen estadístico de los números de insectos recogidos en
los campos tratados con cada tipo de insecticida.
> by ( InsectSprays $ count , InsectSprays $ spray , FUN = summary )
InsectSprays $ spray : A
Min . 1 st Qu . Median Mean 3 rd Qu . Max .
7.00 11.50 14.00 14.50 17.75 23.00
---------------------------------------------------
InsectSprays $ spray : B
Min . 1 st Qu . Median Mean 3 rd Qu . Max .
7.00 12.50 16.50 15.33 17.50 21.00
---------------------------------------------------
InsectSprays $ spray : C
Min . 1 st Qu . Median Mean 3 rd Qu . Max .
0.000 1.000 1.500 2.083 3.000 7.000
---------------------------------------------------
InsectSprays $ spray : D
Min . 1 st Qu . Median Mean 3 rd Qu . Max .
2.000 3.750 5.000 4.917 5.000 12.000
---------------------------------------------------
InsectSprays $ spray : E
Min . 1 st Qu . Median Mean 3 rd Qu . Max .
9-18
1.00 2.75 3.00 3.50 5.00 6.00
---------------------------------------------------
InsectSprays $ spray : F
Min . 1 st Qu . Median Mean 3 rd Qu . Max .
9.00 12.50 15.00 16.67 22.50 26.00
Echando un vistazo a las columnas de las medianas y medias vemos que los insecticidas C, D
y E son, en término medio, más efectivos que A, B y F. Como una imagen vale más que mil
palabras, a continuación dibujamos en un gráfico los diagramas de caja de los valores de count
separados por los niveles de spray.
Obtenemos la Figura 9.9, que muestra a simple vista la misma diferencia en la efectividad de
los insecticidas. También se ve en este gráfico que los números de insectos obtenidos en los
campos tratados con los insecticidas C, D y E presentan una menor variabilidad que el resto,
puesto que sus cajas intercuartílicas son mucho más cortas; lo podemos confirmar calculando
las correspondientes desviaciones típicas muestrales.
> aggregate ( count ~ spray , data = InsectSprays , FUN = sd )
spray count
1 A 4.719399
2 B 4.271115
3 C 1.975225
4 D 2.503028
5 E 1.732051
6 F 6.213378
25
20
Número de insectos
15
10
5
0
A B C D E F
Tipo de insecticida
9-19
9.6. Guía rápida de funciones
table calcula la tabla de frecuencias absolutas de un vector.
prop.table calcula la tabla de frecuencias relativas de un vector a partir de su tabla de
frecuencias absolutas.
cumsum calcula las sumas acumuladas de un vector.
as.vector transforma un objeto en un vector.
mean calcula la media de un vector numérico.
median calcula la mediana de un vector numérico.
as.numeric(names(which(table(x)==max(table(x))))) calcula la moda del vector x.
quantile(x, p) calcula el cuantil de orden p del vector numérico x. El parámetro type
permite especificar el método.
range produce un vector con el mínimo y el máximo de un vector numérico.
IQR calcula el rango intercuartílico de un vector numérico. El parámetro type permite
especificar el método de cálculo de los cuantiles.
var calcula la varianza muestral de un vector numérico.
sd calcula la desviación típica muestral de un vector numérico.
summary, aplicado a un vector numérico, calcula sus extremos, sus cuartiles y su media;
aplicado a un data frame, calcula resúmenes similares para todas sus variables.
by(data frame, factor , FUN=función) aplica la función a las variables del data frame
segmentadas según el factor.
boxplot dibuja los diagramas de caja de los vectores numéricos a los que se aplica.
Algunos parámetros importantes:
• Los de plot que tengan sentido.
• horizontal: igualado a TRUE, dibuja las cajas horizontales.
• names: sirve para especificar los nombres bajo los diagramas de caja en un gráfico
que contenga varios.
• notch: dibuja cinturas alrededor de las medianas que permiten contrastar si las
medianas poblacionales son diferentes.
• plot: igualado a FALSE calcula el diagrama de caja, pero no lo dibuja.
Como objeto de datos, el resultado de esta función es una list entre cuyas componentes
destacamos:
• stats: contiene, para cada diagrama de caja en el gráfico, las ordenadas de sus cinco
líneas horizontales: binf , Q0.25 , Q0.5 , Q0.75 , bsup .
• out: contiene los valores atípicos.
• group: indica los diagramas a los que pertenecen los valores atípicos.
9-20
9.7. Ejercicio
Considerad de nuevo la tabla del ejercicio de la lección anterior, que se encuentra en http://
aprender.uib.es/Rdir/Notas2011A.txt. Definid un data frame con esta tabla, y comprobad
con str y head que el data frame obtenido tiene la estructura deseada.
2. Dibujad en un único gráfico los diagramas de caja de las notas numéricas del examen
de cada grupo; añadid marcas en las notas medias; poned nombres, título, etc., para que
resulte más informativo. ¿Tiene algún grupo algún valor atípico? ¿Podéis decir a partir
de este gráfico en qué grupo hay más variación de notas?
3. Agrupad los estudiantes de los dos grupos de Biología, BLM y BLT, en uno solo, BL, y
repetid los dos puntos anteriores para los grados BL y BQ.
4. ¿Podéis extraer alguna conclusión sobre si el examen ha ido mejor en algún grupo que en
los demás, o mejor en un grado que en el otro?
9-21
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 6
Lección 10
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 10
Descripción de datos cuantitativos
multidimensionales
En general, los datos que se recogen en experimentos son multidimensionales: medimos varias
variables aleatorias sobre una misma muestra de individuos, y organizamos esta información
en tablas de datos en las que las filas representan los individuos observados y cada columna
corresponde a una variable diferente. En lecciones anteriores ya han aparecido datos cualitativos
y ordinales multidimensionales, para los que hemos calculado y representado gráficamente sus
frecuencias globales y marginales; en esta lección estudiamos algunos estadísticos específicos
para resumir y representar la relación existente entre diversas variables cuantitativas.
Este vector está compuesto por las observaciones de las p variables sobre el i-ésimo indi-
viduo.
Esta columna está formada por todos los valores de la j-ésima variable.
Observad que, en cada caso, el punto en el subíndice representa el índice «variable» de los
elementos del vector o de la columna.
De esta manera, podremos expresar la matriz de datos X tanto por filas como por columnas:
0 1
x1•
B x2• C
B C
X = B .. C = (x•1 , x•2 , . . . , x•p ).
@ . A
xn•
Con estas notaciones, podemos generalizar al caso multidimensional los estadísticos de una
variable cuantitativa, definiéndolos como los vectores que se obtienen aplicando el estadístico
concreto a cada columna de la tabla de datos. Así:
El vector de medias de X es el vector formado por las medias aritméticas de sus columnas:
donde n
1X
s2j = (xij x•j )2 .
n i=1
El vector de varianzas muestrales de X está formado por las varianzas muestrales de sus
columnas:
se2X = (e
s21 , se22 , . . . , se2p ),
donde n
1 X n
se2j = (xij x•j )2 = s2j .
n 1 i=1
n 1
10-2
Ejemplo 10.1. Consideremos la tabla de datos
0 1
1 1 3
B1 0 3C
X=B @2
C
3 0A
3 0 1
A veces es conveniente aplicar una transformación lineal a una tabla de datos X, sumando
a cada columna un valor y luego multiplicando cada columna resultante por otro valor. El
ejemplo más común de trasformación lineal es la tipificación de datos.
Dada una variable cuantitativa,1 su variable tipificada es el vector que se obtiene restando a
cada entrada la media aritmética de la variable y dividiendo el resultado por su desviación típica;
de esta manera, la variable tipificada tiene media aritmética 0 y varianza 1. Tipificar una variable
es conveniente cuando se quiere trabajar con sus datos sin que influyan las unidades en los que
están medidos: al dividir por su desviación típica, los valores resultantes son adimensionales.
Tipificar las variables de una tabla de datos permite compararlas dejando de lado las diferen-
cias que pueda haber entre sus valores medios o sus varianzas. Llamaremos matriz tipificada de
1
De ahora en adelante, supondremos que todas las variables cuantitativas que aparezcan en lo que queda de
lección, incluidas las columnas de tablas de datos, son no constantes y, por lo tanto, tienen desviación típica
no nula.
10-3
una matriz de datos X a la matriz Z que se obtiene tipificando cada columna; es decir, para
tipificar una matriz de datos X, primero restamos a cada columna su media aritmética (llama-
remos matriz centrada de X a la matriz obtenida en este paso), y, a continuación, dividimos
cada columna de la matriz centrada por su desviación típica, que coincide con la desviación
típica de la columna original de X:
0 x11 x•1 x12 x•2 1
s1 s2
. . . x1pspx•p
B C
B x21 x•1 x22 x•2 x2p x•p C
B s1 . . . C
Z=B C.
s2 sp
B C
B .. .. .. .. C
@ . . . . A
xn1 x•1 xn2 x•2 xnp x•p
s1 s2
... sp
La manera más sencilla de aplicar una transformación lineal a una tabla de datos X, y en
particular de tipificarla, es usando la instrucción
scale(X, center=..., scale=...)
donde:
X puede ser tanto una matriz como un data frame; el resultado será un objeto de la misma
clase.
El valor del parámetro center es el vector que restamos a sus columnas, en el sentido de
que cada entrada de este vector se restará a todas las entradas de la columna correspon-
diente. Su valor por defecto (que no es necesario especificar, aunque también se puede
especificar con center=TRUE) es el vector X de medias de X; para especificar que no se
reste nada, podemos usar center=FALSE.
El valor del parámetro scale es el vector por el que dividimos las columnas de la matriz
obtenida tras restar el valor de center: cada columna se divide por la entrada correspon-
diente de este vector. Su valor por defecto (de nuevo, se puede especificar igualando el
parámetro a TRUE) es el vector seX de desviaciones típicas muestrales; para especificar que
no se divida por nada, podemos usar scale=FALSE.
En particular, la instrucción scale(X) centra la tabla de datos X y divide sus columnas por
sus desviaciones típicas muestrales; por lo tanto, no la tipifica según nuestra definición, ya que
no las divide por sus desviaciones típicas «verdaderas».
10-4
[2 , ] - 0.75 - 0.5 1.25
[3 , ] 0.25 2.5 - 1.75
[4 , ] 1.25 - 0.5 - 0.75
attr ( , " scaled : center " )
V1 V2 V3
1.75 0.50 1.75
Observad la estructura del resultado: en primer lugar nos da la matriz centrada, y a continuación
nos dice que tiene un atributo llamado "scaled:center" cuyo valor es el vector usado para
centrarla. Este atributo no interferirá para nada en las operaciones que se realicen con la matriz
centrada, pero, si os molesta, recordad de la Lección 3 que se puede eliminar sustituyendo el
resultado de centrar la matriz en los puntos suspensivos de la instrucción siguiente:
attr(..., "scaled:center")=NULL.
Como ya hemos avisado, para tipificar esta tabla de datos no podemos hacer lo siguiente:
> X _ tip = scale ( X )
> X _ tip
V1 V2 V3
[1 , ] - 0.7833495 - 0.8660254 0.8333333
[2 , ] - 0.7833495 - 0.2886751 0.8333333
[3 , ] 0.2611165 1.4433757 - 1.1666667
[4 , ] 1.3055824 - 0.2886751 - 0.5000000
attr ( , " scaled : center " )
V1 V2 V3
1.75 0.50 1.75
attr ( , " scaled : scale " )
V1 V2 V3
0.9574271 1.7320508 1.5000000
10-5
V1 V2 V3
[1 , ] - 0.9045340 - 1.0000000 0.9622504
[2 , ] - 0.9045340 - 0.3333333 0.9622504
[3 , ] 0.3015113 1.6666667 - 1.3471506
[4 , ] 1.5075567 - 0.3333333 - 0.5773503
attr ( , " scaled : center " )
V1 V2 V3
1.75 0.50 1.75
attr ( , " scaled : scale " )
V1 V2 V3
0.9574271 1.7320508 1.5000000
Otra posibilidad es usar, como valor del parámetro scale, el vector sX de desviaciones típicas
de las columnas.
> sd _ ver = function ( x ) { sqrt ( var ( x ) * ( length ( x ) - 1) / length ( x ) ) }
> X _ dtv = sapply (X , sd _ ver ) # Desviaciones t í picas " verdaderas "
> X _ tip1 = scale (X , scale = X _ dtv ) # Escalamos dividiendo las columnas
por X _ dtv
> X _ tip1
V1 V2 V3
[1 , ] - 0.9045340 - 1.0000000 0.9622504
[2 , ] - 0.9045340 - 0.3333333 0.9622504
[3 , ] 0.3015113 1.6666667 - 1.3471506
[4 , ] 1.5075567 - 0.3333333 - 0.5773503
attr ( , " scaled : center " )
V1 V2 V3
1.75 0.50 1.75
attr ( , " scaled : scale " )
V1 V2 V3
0.8291562 1.5000000 1.2990381
Observaréis que la matriz resultante es la misma, pero el atributo que indica el vector por
el que hemos dividido las columnas es diferente: ahora ha sido el de desviaciones típicas.
En ambos casos, podemos usar la función attr para eliminar uno a uno los dos atributos,
"scaled:center" y "scaled:scale", que se han añadido a la matriz tipificada.
> attr ( X _ tip , " scaled : center " ) = NULL
> attr ( X _ tip , " scaled : scale " ) = NULL
> X _ tip
V1 V2 V3
[1 , ] - 0.9045340 - 1.0000000 0.9622504
[2 , ] - 0.9045340 - 0.3333333 0.9622504
[3 , ] 0.3015113 1.6666667 - 1.3471506
[4 , ] 1.5075567 - 0.3333333 - 0.5773503
10-6
decrece, la otra tiene el mismo comportamiento; en cambio, cuando la covarianza es negativa,
esta tendencia se invierte: si una variable crece, la otra decrece y viceversa. Por desgracia,
interpretar el valor de la covarianza más allá de su signo es difícil, por lo que introduciremos
una versión «normalizada» de la misma, la correlación de Pearson, que mide de manera más
precisa la relación lineal entre dos variables.
La covarianza generaliza la varianza, en el sentido de que la varianza de una variable es su
covarianza consigo misma. Y como en el caso de la varianza, definiremos dos versiones de la
covarianza: la «verdadera», que se usa para medir la tendencia a la variación conjunta de dos
conjuntos específicos de datos, y la muestral, que además aproxima mejor la covarianza de las
variables definidas sobre la población total. La diferencia estará de nuevo en el denominador.
Formalmente, la covarianza de las variables x•i y x•j de una matriz de datos X es
1⇣X ⌘
n n
1X
sij = (xki x•i )(xkj x•j ) = xki xkj x•i x•j ,
n k=1 n k=1
y su covarianza muestral es
n
X
1 n
seij = (xki x•i )(xkj x•j ) = sij .
n 1 k=1
n 1
Es inmediato comprobar a partir de sus definiciones que ambas covarianzas son simétricas,
y que la covarianza, tanto «verdadera» como muestral, de una variable consigo misma es su
correspondiente varianza:
3 0 1
del Ejemplo 10.1 se calcularía de la manera siguiente, teniendo en cuenta que sus medias son
1.75 y 0.5, respectivamente:
1
s12 = (1 · ( 1) + 1 · 0 + 2 · 3 + 3 · 0) 1.75 · 0.5 = 1.25 0.875 = 0.375.
4
Su covarianza muestral se obtendría multiplicando este valor por 4/3:
4
se12 = s12 = 0.5.
3
10-7
1 1 -1 3
2 1 0 3
3 2 3 0
4 3 0 1
> cov ( X $ V1 , X $ V2 ) # Covarianza MUESTRAL
[1] 0.5
> (3 / 4) * cov ( X $ V1 , X $ V2 ) # Covarianza " verdadera "
[1] 0.375
Queremos recalcar que, como en el caso de la varianza con var, R calcula con cov la versión
muestral de la covarianza.
Las matrices de covarianzas y de covarianzas muestrales de una tabla de datos X son, res-
pectivamente,
0 1 0 1
s11 s12 . . . s1p se11 se12 . . . se1p
Bs21 s22 . . . s2p C Bse21 se22 . . . se2p C
B C e=B C
S = B .. .. .. .. C , S B .. .. .. .. C ,
@ . . . . A @ . . . . A
sp1 sp2 . . . spp sep1 sep2 . . . sepp
donde cada sij y cada seij son, respectivamente, la covarianza y la covarianza muestral de las
correspondientes columnas x•i y x•j . Estas matrices de covarianzas miden la tendencia a la
variabilidad conjunta de los datos de X.
La matriz de covarianzas muestrales de X se calcula aplicando la función cov al data frame
o a la matriz que contenga dicha tabla. Para obtener su matriz de covarianzas «verdaderas»,
es suficiente multiplicar el resultado de cov por (n 1)/n, donde n es el número de filas de X.
> X
V1 V2 V3
1 1 -1 3
2 1 0 3
3 2 3 0
4 3 0 1
> n = dim ( X ) [1]
> cov ( X ) # Matriz de covarianzas muestrales
V1 V2 V3
V1 0.9166667 0.500000 - 1.083333
V2 0.5000000 3.000000 - 2.166667
V3 - 1.0833333 - 2.166667 2.250000
> (( n - 1) / n ) * cov ( X ) # Matriz de covarianzas " verdaderas "
V1 V2 V3
V1 0.6875 0.375 - 0.8125
V2 0.3750 2.250 - 1.6250
V3 - 0.8125 - 1.625 1.6875
10-8
por lo que esta correlación se puede calcular también a partir de las versiones muestrales de la
covarianza y las desviaciones típicas por medio de la misma fórmula.
La correlación rij tiene las propiedades siguientes:
Así pues, la correlación entre dos variables viene a ser una covarianza «normalizada», ya que,
como vemos, su valor está entre 1 y 1, y mide la tendencia de las variables a estar relacionadas
según una función lineal. En concreto, cuanto más se acerca la correlación a 1 (respectivamente,
a 1), más se acerca una (cualquiera) de las variables a ser función lineal creciente (respecti-
vamente, decreciente) de la otra.
Con R, la correlación de Pearson de dos vectores se puede calcular por medio de la función
cor.
Ejemplo 10.4. En ejemplos anteriores hemos calculado la covarianza y las varianzas de las
dos primeras columnas de la matriz de datos
0 1
1 1 3
B1 0 3C
X=B @2
C.
3 0A
3 0 1
Hemos obtenido los valores siguientes:
p
11 3
s12 = 0.375, s1 = = 0.82916, s2 = = 1.5.
4 2
Por lo tanto, su correlación es
0.375
r12 = = 0.3015.
0.82916 · 1.5
Ahora vamos a calcularla con R, y aprovecharemos para confirmar su relación con el valor de
R2 de la regresión lineal de la segunda columna respecto de la primera.
10-9
> X
V1 V2 V3
1 1 -1 3
2 1 0 3
3 2 3 0
4 3 0 1
> cor ( X $ V1 , X $ V2 )
[1] 0.3015113
> cor ( X $ V1 , X $ V2 ) ^2
[1] 0.09090909
> summary ( lm ( X $ V2 ~ X $ V1 ) ) $ r . squared
[1] 0.09090909
La matriz de correlaciones de X es
0 1
1 r12 . . . r1p
Br21 1 . . . r2p C
B C
R = B .. .. .. .. C ,
@ . . . . A
rp1 rp2 ... 1
donde cada rij es la correlación de las columnas correspondientes de X. Esta matriz de correla-
ciones se puede calcular con R con la misma instrucción cor, que se puede aplicar tanto a una
matriz como a un data frame.
> X
V1 V2 V3
1 1 -1 3
2 1 0 3
3 2 3 0
4 3 0 1
> cor ( X )
V1 V2 V3
V1 1.0000000 0.3015113 - 0.7543365
V2 0.3015113 1.0000000 - 0.8339504
V3 - 0.7543365 - 0.8339504 1.0000000
Se tiene el teorema siguiente, que se puede demostrar mediante un simple, aunque farragoso,
cálculo algebraico:
Teorema 10.1. La matriz de correlaciones de X es igual a la matriz de covarianzas de su
matriz tipificada.
10-10
La importancia de este resultado es que, si la tabla de datos es muy grande, suele ser más
eficiente tipificar primero la tabla y luego calcular la matriz de covarianzas de la tabla tipificada
que calcular directamente la matriz de correlaciones de la tabla original. Comprobemos que el
teorema es cierto para nuestra matriz de datos.
> X
V1 V2 V3
1 1 -1 3
2 1 0 3
3 2 3 0
4 3 0 1
> n = dim ( X ) [1]
> X _ tip = scale ( X ) * sqrt ( n / ( n - 1) )
> cov ( X _ tip ) * ( n - 1) / n
V1 V2 V3
V1 1.0000000 0.3015113 - 0.7543365
V2 0.3015113 1.0000000 - 0.8339504
V3 - 0.7543365 - 0.8339504 1.0000000
Cuando se calcula la covarianza o la correlación de dos vectores que contienen NA, lo usual
es no tenerlos en cuenta: es decir, si un vector contiene un NA en una posición, se eliminan de
ambos vectores sus entradas en dicha posición. De esta manera, se tomaría como covarianza de
0 1 0 1
1 2
B 2 C B 4 C
B C B C
B NA C B C
B C y B 3 C
B 4 C B 5 C
B C B C
@ 6 A @ 7 A
2 NA
la de 0 1 0 1
1 2
B 2 C B 4 C
B C y B C.
@ 4 A @ 5 A
6 7
Al aplicar cov o cor a un par de vectores que contengan NA, se obtiene, por defecto, NA. Si
se quiere que R calcule el valor sin tener en cuenta los NA, se ha de especificar añadiendo el
parámetro use="complete.obs" (que le indica que ha de usar las observaciones completas, es
decir, las posiciones que no tienen NA en ninguno de los dos vectores).
> x = c (1 ,2 , NA ,4 ,6 ,2)
> y = c (2 ,4 , - 3 ,5 ,7 , NA )
> x1 = c (1 ,2 ,4 ,6) # Quitamos las entradas 3 a y 6 a
> x2 = c (2 ,4 ,5 ,7) # Quitamos las entradas 3 a y 6 a
> cov (x , y )
[1] NA
> cov (x , y , use = " complete . obs " )
[1] 4.5
> cov ( x1 , x2 )
[1] 4.5
> cor (x , y )
10-11
[1] NA
> cor (x , y , use = " complete . obs " )
[1] 0.9749135
> cor ( x1 , x2 )
[1] 0.9749135
Antes de nada, se eliminan las filas de la tabla que contienen algún NA en alguna columna,
dejando solo en la tabla las filas «completas», las que no contienen ningún NA. Luego se
calcula la matriz de covarianzas o de correlaciones de la tabla resultante. Esta opción se
especifica con el parámetro use="complete.obs".
Veamos un ejemplo:
> X = cbind ( c (1 ,2 , NA ,4 ,6 ,2) , c (2 ,4 , - 3 ,5 ,7 , NA ) , c ( - 2 ,1 ,0 ,2 ,3 ,0) )
> X
[ , 1] [ , 2] [ , 3]
[1 , ] 1 2 -2
[2 , ] 2 4 1
[3 , ] NA -3 0
[4 , ] 4 5 2
[5 , ] 6 7 3
[6 , ] 2 NA 0
> cov ( X )
[ , 1] [ , 2] [ , 3]
[1 , ] NA NA NA
[2 , ] NA NA NA
[3 , ] NA NA 3.066667
> cov (X , use = " pairwise . complete . obs " )
[ , 1] [ , 2] [ , 3]
[1 , ] 4.0 4.50 3.500000
[2 , ] 4.5 14.50 4.750000
[3 , ] 3.5 4.75 3.066667
> cov (X , use = " complete . obs " )
[ , 1] [ , 2] [ , 3]
[1 , ] 4.916667 4.500000 4.333333
[2 , ] 4.500000 4.333333 4.333333
[3 , ] 4.333333 4.333333 4.666667
> Y = cbind ( c (1 ,2 ,4 ,6) , c (2 ,4 ,5 ,7) , c ( - 2 ,1 ,2 ,3) ) # Eliminamos las
filas con alg ú n NA
> Y
10-12
[ , 1] [ , 2] [ , 3]
[1 , ] 1 2 -2
[2 , ] 2 4 1
[3 , ] 4 5 2
[4 , ] 6 7 3
> cov ( Y ) # Dar á lo mismo que con use =" complete . obs "
[ , 1] [ , 2] [ , 3]
[1 , ] 4.916667 4.500000 4.333333
[2 , ] 4.500000 4.333333 4.333333
[3 , ] 4.333333 4.333333 4.666667
Ejemplo 10.5. Recordaréis el data frame iris, que tabulaba las longitudes y anchuras de
los pétalos y los sépalos de una muestra de flores iris de tres especies. Vamos a extraer un
subdata frame con sus cuatro variables numéricas y calcularemos sus matrices de covarianzas
y correlaciones.
> str ( iris )
’ data . frame ’: 150 obs . of 5 variables :
$ Sepal . Length : num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal . Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal . Length : num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal . Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w / 3 levels " setosa " , " versicolor " , ..: 1 1
1 1 1 1 1 1 1 1 ...
> iris _ num = iris [ , 1:4]
> cov ( iris _ num ) # Covarianzas muestrales
Sepal . Length Sepal . Width Petal . Length Petal . Width
Sepal . Length 0.6856935 - 0.0424340 1.2743154 0.5162707
Sepal . Width - 0.0424340 0.1899794 - 0.3296564 - 0.1216394
Petal . Length 1.2743154 - 0.3296564 3.1162779 1.2956094
Petal . Width 0.5162707 - 0.1216394 1.2956094 0.5810063
> n = dim ( iris _ num ) [1] # N ú mero de filas ; son 150 , recordemos
> cov ( iris _ num ) * ( n - 1) / n # Covarianzas " verdaderas "
Sepal . Length Sepal . Width Petal . Length Petal . Width
Sepal . Length 0.68112222 - 0.04215111 1.2658200 0.5128289
Sepal . Width - 0.04215111 0.18871289 - 0.3274587 - 0.1208284
Petal . Length 1.26582000 - 0.32745867 3.0955027 1.2869720
Petal . Width 0.51282889 - 0.12082844 1.2869720 0.5771329
> cor ( iris _ num ) # Correlaciones
Sepal . Length Sepal . Width Petal . Length Petal . Width
Sepal . Length 1.0000000 - 0.1175698 0.8717538 0.8179411
Sepal . Width - 0.1175698 1.0000000 - 0.4284401 - 0.3661259
Petal . Length 0.8717538 - 0.4284401 1.0000000 0.9628654
Petal . Width 0.8179411 - 0.3661259 0.9628654 1.0000000
Observamos, por ejemplo, una gran correlación lineal positiva entre la longitud y la anchura
de los pétalos, 0.9628654, lo que indica una estrecha relación lineal con pendiente positiva entre
estas magnitudes. Valdría la pena, entonces, calcular la recta de regresión lineal de una de estas
medidas en función de la otra:
> lm ( Petal . Length ~ Petal . Width , data = iris _ num )
10-13
Call :
lm ( formula = Petal . Length ~ Petal . Width , data = iris _ num )
Coefficients :
( Intercept ) Petal . Width
1.084 2.230
> summary ( lm ( Petal . Length ~ Petal . Width , data = iris _ num ) ) $ r . squared
[1] 0.9271098
10-14
Vemos que el par de variables con mayor correlación en valor absoluto son Petal.Length y
Petal.Width, como ya habíamos observado, seguidos por Petal.Length y Sepal.Length.
Vamos a explicar el código. La función upper.tri, aplicada a una matriz cuadrada M ,
produce la matriz triangular superior de valores lógicos del mismo orden que M , cuyas entradas
(i, j) con i < j son todas TRUE y el resto todas FALSE. Existe una función similar, lower.tri,
para producir matrices triangulares inferiores de valores lógicos.
> upper . tri ( diag (4) )
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] FALSE TRUE TRUE TRUE
[2 ,] FALSE FALSE TRUE TRUE
[3 ,] FALSE FALSE FALSE TRUE
[4 ,] FALSE FALSE FALSE FALSE
> lower . tri ( diag (4) )
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] FALSE FALSE FALSE FALSE
[2 ,] TRUE FALSE FALSE FALSE
[3 ,] TRUE TRUE FALSE FALSE
[4 ,] TRUE TRUE TRUE FALSE
Ambas funciones disponen del parámetro diag que, igualado a TRUE, define también como TRUE
las entradas de la diagonal principal.
> upper . tri ( diag (4) , diag = TRUE )
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] TRUE TRUE TRUE TRUE
[2 ,] FALSE TRUE TRUE TRUE
[3 ,] FALSE FALSE TRUE TRUE
[4 ,] FALSE FALSE FALSE TRUE
Si M es una matriz y L es una matriz de valores lógicos del mismo orden, M[L] produce el
vector construido de la manera siguiente: de cada columna, se queda sólo con las entradas de
M cuya entrada correspondiente en L es TRUE, y a continuación concatena estas columnas, de
izquierda a derecha, en un vector.
> M = matrix (1:16 , nrow =4 , byrow = T )
> M
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] 1 2 3 4
[2 ,] 5 6 7 8
[3 ,] 9 10 11 12
[4 ,] 13 14 15 16
> M [ upper . tri ( diag (4) ) ] # Las entradas del tri á ngulo superior , por
columnas
[1] 2 3 7 4 8 12
10-15
[3 ,] " Petal . Length " " Petal . Length " " Petal . Length " " Petal . Length "
[4 ,] " Petal . Width " " Petal . Width " " Petal . Width " " Petal . Width "
> matrix ( rep ( medidas , times =4) , nrow =4 , byrow = TRUE )
[ ,1] [ ,2] [ ,3] [ ,4]
[1 ,] " Sepal . Length " " Sepal . Width " " Petal . Length " " Petal . Width "
[2 ,] " Sepal . Length " " Sepal . Width " " Petal . Length " " Petal . Width "
[3 ,] " Sepal . Length " " Sepal . Width " " Petal . Length " " Petal . Width "
[4 ,] " Sepal . Length " " Sepal . Width " " Petal . Length " " Petal . Width "
El resultado es la Figura 10.1, que muestra una clara tendencia positiva: cuanto más largos son
los pétalos, más anchos tienden a ser. Esto se corresponde con la correlación de 0.9628654 que
hemos obtenido en el Ejemplo 10.5.
Para tablas de datos de tres columnas numéricas, podemos usar con un fin similar la instruc-
ción scatterplot3d del paquete homónimo, que dibuja un diagrama de dispersión tridimensio-
10-16
2.5
2.0
1.5
Ancho
1.0
0.5
1 2 3 4 5 6 7
Largo
nal. Como plot, se puede aplicar a un data frame o a una matriz; por ejemplo, para representar
gráficamente las tres primeras variables numéricas de iris, podríamos hacer lo siguiente:
Obtendríamos la Figura 10.2. Podéis consultar la Ayuda de la instrucción para saber cómo
modificar su apariencia: cómo ponerle un título, etiquetar los ejes, usar colores, cambiar el
estilo del gráfico, etc.
7
6
5
Petal.Length
Sepal.Width
4.5
3
4.0
3.5
2
3.0
2.5
2.0
1
4 5 6 7 8
Sepal.Length
Una representación gráfica muy popular de las tablas de datos de tres o más columnas numé-
ricas son las matrices formadas por los diagramas de dispersión de todos sus pares de columnas.
10-17
Si la tabla de datos es un data frame, esta matriz de diagramas de dispersión se obtiene sim-
plemente aplicando la función plot al data frame; por ejemplo,
> plot ( iris [ , 1:4])
produce el gráfico de la izquierda de la Figura 10.3. En este gráfico, los cuadrados en la dia-
gonal indican a qué variables corresponden cada fila y cada columna, de manera que podamos
identificar fácilmente qué variables compara cada diagrama de dispersión; así, en el diagrama
de la primera fila y segunda columna de esta figura, las abscisas corresponden a anchuras de
sépalos y las ordenadas a longitudes de sépalos. Observad que la nube de puntos no muestra una
tendencia clara y en todo caso ligeramente negativa, lo que se corresponde con la correlación
entre estas variables de 0.11 que hemos obtenido en el Ejemplo 10.5.
Podemos usar los parámetros usuales de plot para mejorar el gráfico resultante; por ejemplo,
podemos usar colores para distinguir las flores según su especie:
> plot ( iris [ , 1:4] , col = iris $ Species )
2.0 2.5 3.0 3.5 4.0 0.5 1.0 1.5 2.0 2.5 2.0 2.5 3.0 3.5 4.0 0.5 1.0 1.5 2.0 2.5
7.5
7.5
6.5
6.5
Sepal.Length Sepal.Length
5.5
5.5
4.5
4.5
2.0 2.5 3.0 3.5 4.0
Sepal.Width Sepal.Width
7
7
6
6
5
5
Petal.Length Petal.Length
4
4
3
3
2
2
1
1
0.5 1.0 1.5 2.0 2.5
Petal.Width Petal.Width
10-18
produce el gráfico de la izquierda de la Figura 10.4. Observaréis para empezar que en los
cuadrados de la diagonal ha dibujado unas curvas: se trata de la curva de densidad de la variable
correspondiente de la que ya hablábamos en la Sección 11.4. La información gráfica contenida
en estos cuadrados de la diagonal se puede modificar con el parámetro diagonal: podemos
pedir, por ejemplo, que dibuje un histograma de cada variable (con diagonal="histogram") o
su boxplot (con diagonal="boxplot"). Así,
> spm ( iris [ , 1:4] , diagonal = " boxplot " )
2.0 2.5 3.0 3.5 4.0 0.5 1.0 1.5 2.0 2.5 2.0 2.5 3.0 3.5 4.0 0.5 1.0 1.5 2.0 2.5
Sepal.Length Sepal.Length
7.5
7.5
6.5
6.5
5.5
5.5
4.5
4.5
Sepal.Width Sepal.Width
2.0 2.5 3.0 3.5 4.0
7
Petal.Length Petal.Length
6
6
5
5
4
4
3
3
2
2
1
1
0.5 1.0 1.5 2.0 2.5
Petal.Width Petal.Width
Asimismo, observaréis que los diagramas de dispersión de la matriz producida con spm con-
tienen curvas. La línea recta verde es la recta de regresión por mínimos cuadrados y, sin entrar
en detalle sobre su significado exacto, las curvas rojas continuas representan la tendencia de los
datos.
10-19
• Igualado a "complete.obs", calcula las covarianzas de las columnas teniendo en
cuenta sólo las filas completas de toda la matriz.
cor, aplicada a dos vectores, calcula su correlación; aplicada a un data frame o a una
matriz, calcula su matriz de correlaciones. Dispone del mismo parámetro use que cov.
order ordena el primer vector al que se aplica, desempatando empates mediante el orden
de los vectores subsiguientes a los que se aplica; el parámetro decreasing=TRUE sirve
para especificar que sea en orden decreciente.
plot, aplicado a un data frame de dos variables numéricas, dibuja su diagrama de dis-
persión; aplicado a un data frame de más de dos variables numéricas, produce la matriz
formada por los diagramas de dispersión de todos sus pares de variables.
scatterplot3d, del paquete del mismo nombre, dibuja diagramas de dispersión tridi-
mensionales.
10.5. Ejercicio
El fichero http://aprender.uib.es/Rdir/NotasMatesI14.txt recoge las notas medias (sobre
100) obtenidas, en las diferentes actividades de evaluación de la asignatura Matemáticas I del
grado de Biología en el curso 2013/14, por parte de los estudiantes que fueron considerados
«presentados» en la primera convocatoria. Estas actividades consistieron en:
(a) Calculad el vector de medias y el vector de desviaciones típicas de esta tabla de datos.
¿Cuáles son las actividades de evaluación cuyas notas presentan mayor y menor variabili-
dad?
10-20
(c) ¿Qué variable tiene la mayor correlación media con las otras variables? ¿Cuál tiene la
menor?
(d) Ordenad los pares de variables de esta tabla por su correlación. ¿Cuáles son los dos pares
con mayor correlación positiva? ¿Cuáles son los dos pares con menor correlación negativa?
(e) Comprobad en esta tabla de datos que su matriz de correlaciones es igual a la matriz de
covarianzas de su tabla tipificada.
(f) Dibujad una matriz de diagramas de dispersión de estas notas. ¿Se pueden ver en este
diagrama los dos pares de actividades de evaluación con mayor correlación y los dos pares
con menor correlación que habéis encontrado en el apartado (d)?
10-21
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 7
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 11
Datos cuantitativos agrupados
En nuestro lenguaje cotidiano, solemos agrupar datos cuantitativos sin que seamos conscientes
de ello. Cuando decimos, por ejemplo, que la edad de alguien es de 18 años, no queremos decir
que nació justo hoy hace 18 años, sino que ya ha cumplido los 18 años, pero aún no ha cumplido
los 19; es decir, que agrupamos todas las edades que caen dentro del intervalo [18, 19) en una
misma clase, que llamamos «18 años». Del mismo modo, que alguien mida 1.72 no significa
que esta sea su altura exacta, con la precisión del grueso de un cabello, sino que su altura
pertenece a un intervalo de valores en torno a 1.72 metros que identificamos con «1.72». Bajo
la calificación de «aprobado» agrupamos todas las notas mayores o iguales que 5 y menores que
7. Y estamos seguros de que se os ocurren otros ejemplos.
Cuando trabajamos en estadística con datos cuantitativos, puede haber varios motivos por
los que nos interese agruparlos. Una posibilidad es que queramos estudiar la distribución de una
cierta variable (pongamos, la altura) en una muestra de individuos, y que los valores que pueda
tomar esta variable sean muy heterogéneos; en esta situación, lo normal sería que obtuviéramos
muy pocas repeticiones, por lo que las frecuencias de los valores individuales serían muy bajas
y por lo tanto muy similares. Esto daría lugar a un diagrama de barras difícil de interpretar.
Veamos un ejemplo: consideremos la siguiente muestra de 30 alturas de estudiantes:
1.71, 1.62, 1.72, 1.76, 1.78, 1.73, 1.67, 1.64, 1.63, 1.68, 1.68, 1.70, 1.67, 1.56, 1.66, 1.57, 1.69,
1.68, 1.67, 1.75, 1.61, 1.60, 1.74, 1.70, 1.65, 1.55, 1.82, 1.70, 1.69, 1.81.
El diagrama de barras de sus frecuencias (tomando como posibles niveles todas las alturas entre
su mínimo y su máximo, redondeadas a cm) es la Figura 11.1.(a). Todas la barras tienen alturas
entre 0 y 3, y salvo una mayor presencia de los valores centrales (entre 1.67 y 1.70), no hay
mucho más que salte a la vista en este gráfico.
En situaciones como esta, es recomendable dividir los posibles valores de la variable en in-
tervalos y contar cuántos datos caen dentro de cada intervalo: habitualmente, las frecuencias
que se obtienen de esta manera son más fáciles de interpretar que las de los datos individuales.
Así, siguiendo con nuestro ejemplo de las alturas, el diagrama de barras de la Figura 11.1.(b)
representa sus frecuencias cuando las agrupamos en intervalos de 5 cm. La distribución de estas
alturas es mucho más fácil de entender mediante este gráfico que con el primero.
Otro motivo por el que puede ser conveniente agrupar datos es la imposibilidad física de
medir de manera exacta algunas magnitudes continuas como alturas, pesos o tiempos; esto
hace que los datos obtenidos sean sólo aproximaciones o redondeos de los valores reales y que
cada medida diferente represente todo un intervalo de posibles valores.
En general, hay tres situaciones concretas en las cuales conviene agrupar datos cuantitativos
en intervalos de valores, también llamados clases:
Cuando los datos son continuos y no se pueden medir de manera exacta: su redondeo ya
define un agrupamiento.
Cuando los datos son discretos, pero con un número muy grande de posibles valores:
números de aminoácidos en proteínas, números de bases en cadenas de ADN. . .
10
3
8
2
6
4
1
2
0
0
1.55 1.58 1.61 1.64 1.67 1.7 1.73 1.76 1.79 1.82 [1.55,1.6) [1.6,1.65) [1.65,1.7) [1.7,1.75) [1.75,1.8) [1.8,1.85)
(a) (b)
Figura 11.1. Diagramas de barras de un mismo conjunto de
alturas: (a) con los datos sin agrupar; (b) con los datos agru-
pados en intervalos de 5 cm.
Cuando tenemos muchos datos y nos interesa estudiar las frecuencias de sus valores; hay
autores que consideran que muchos, en este contexto, significa 30 o más.
No hay una manera de agrupar datos mejor que otra; por ejemplo, para estudiar las califica-
ciones de un curso podemos agruparlas en Suspenso, [0, 5), Aprobado, [5, 7) Notable, [7, 9), y
Sobresaliente, [9, 10], o podemos redondear por defecto las notas a su parte entera y usar los
intervalos [0, 1), [1, 2), . . . , [9, 10]. Podría ser que cada uno de estos agrupamientos saque a la
luz características diferentes del conjunto de datos.
La función básica de R para estudiar datos agrupados, hist, implementa todo el proceso:
si le entramos el vector de datos y el número de intervalos, o el método para determinarlo
(véase más adelante), agrupará los datos en, más o menos, el número de clases que le hemos
especificado, sin ningún control por nuestra parte sobre los intervalos que produce. Para un
análisis somero de los datos, esto suele ser más que suficiente, pero para una descripción más
cuidadosa es conveniente que seamos nosotros quienes controlemos el proceso de agrupamiento,
11-2
y en particular que calculemos los extremos de los intervalos, en lugar de dejárselos calcular a
R. En esta sección explicamos nuestra receta para agrupar datos y calcular marcas de clase; no
es ni mejor ni peor que otras, pero es la que os recomendamos que uséis, sobre todo en el test
si queréis obtener las respuestas correctas.
Lo primero que tenemos que hacer es establecer el número k de clases en las que vamos a
dividir el conjunto de datos, al que denotaremos en lo que sigue por x. Podemos decidir este
número de clases en función de nuestros intereses concretos, o podemos usar alguna de las
reglas que se han propuesto con este fin; las más populares son las siguientes, donde n denota
el número de datos en la muestra:1
⌃p ⌥
Regla de la raíz cuadrada: k = n .
⌃ ⌥
Regla de Sturges: k = 1 + log2 (n) .
Regla de Scott: Se determina primero la amplitud teórica AS de las clases mediante la
fórmula
1
AS = 3.5 · se · n 3
(donde se es la desviación típica muestral del conjunto de datos), y entonces se toma
⇠ ⇡
máx(x) mı́n(x)
k= .
AS
Regla de Freedman-Diaconis: Se determina primero la amplitud teórica AF D de las clases
por medio de la fórmula
1
AF D = 2 · (Q0.75 Q0.25 ) · n 3
(recordad que Q0.75 Q0.25 es el rango intercuartílico), y entonces se toma de nuevo
⇠ ⇡
máx(x) mı́n(x)
k= .
AF D
Como podéis ver, las dos primeras sólo dependen de n, mientras que las dos últimas tienen en
cuenta, de maneras diferentes, su dispersión; no hay una regla mejor que las otras y, además,
números de clases diferentes pueden revelar características diferentes de los datos. Las tres últi-
mas reglas están implementadas en las funciones nclass.Sturges, nclass.scott y nclass.FD
de R, respectivamente.
Ejemplo 11.1. Mucha gente manifiesta reacciones alérgicas sistémicas a las picaduras de in-
secto; estas reacciones varían entre pacientes, no sólo en lo que se refiere a la gravedad de la
reacción, sino también en el tiempo que tarda en manifestarse. En un estudio se midió, en
40 pacientes que experimentaron una reacción alérgica a una picadura de abeja, el tiempo de
inicio de esta reacción desde la picadura, y se obtuvieron los datos siguientes, que expresamos
en minutos:
10.5, 11.2, 9.9, 15.0, 11.4, 12.7, 16.5, 10.1, 12.7, 11.4, 11.6, 6.2, 7.9, 8.3, 10.9, 8.1, 3.8, 10.5,
11.7, 8.4, 12.5, 11.2, 9.1, 10.4, 9.1, 13.4, 12.3, 5.9, 11.4, 8.8, 7.4, 8.6, 13.6, 14.7, 11.5, 11.5,
10.9, 9.8, 12.9, 9.9.
Según las diferentes reglas que hemos explicado, los números de intervalos en los que tendría-
mos que dividir estos datos son los siguientes:
1
Recordad en lo que sigue que dxe denota el menor entero que es mayor o igual que x, y que con R se calcula
mediante la función ceiling.
11-3
p
Regla de la raíz cuadrada: k = d 40e = d6.3245e = 7.
Regla de Sturges: k = d1 + log2 (40)e = d6.321928e = 7.
Regla de Scott: Mediante
> alergia = c (10.5 ,11.2 ,9.9 ,15.0 ,11.4 ,12.7 ,16.5 ,10.1 ,12.7 ,11.4 ,11.6 ,
6.2 ,7.9 ,8.3 ,10.9 ,8.1 ,3.8 ,10.5 ,11.7 ,8.4 ,12.5 ,11.2 ,9.1 ,10.4 ,9.1 ,
13.4 ,12.3 ,5.9 ,11.4 ,8.8 ,7.4 ,8.6 ,13.6 ,14.7 ,11.5 ,11.5 ,10.9 ,9.8 ,
12.9 ,9.9)
> sd ( alergia )
[1] 2.533609
> diff ( range ( alergia ) )
[1] 12.7
vemos que la desviación típica muestral de estos datos es se = 2.533609 y su rango, 12.7;
1
por lo tanto, AS = 3.5·2.533609·40 3 = 2.592911 y k = d12.7/2.592911e = d4.89797e = 5.
Regla de Freedman-Diaconis: Mediante
> IQR ( alergia )
[1] 2.825
1
obtenemos que Q0.75 Q0.25 = 2.825; por lo tanto, AF D = 2 · 2.825 · 40 3 = 1.65207 y
k = d12.7/1.65207e = d7.687e = 8.
Como podéis ver, reglas diferentes pueden dar valores diferentes, y puede que no.
Con R, hubiéramos podido calcular directamente los tres últimos números de clases de la
manera siguiente:
> nclass . Sturges ( alergia )
[1] 7
> nclass . scott ( alergia )
[1] 5
> nclass . FD ( alergia )
[1] 8
Una vez determinado el número k de clases, tenemos que decidir su amplitud; la forma más
sencilla, y que adoptaremos por defecto, es tomar todos los intervalos de la misma amplitud.2
Para calcular esta amplitud, A, dividiremos el rango de los datos entre el número k de clases y
redondearemos por exceso a un valor de la precisión de la medida: si medimos edades con una
precisión de años, redondearemos este cociente por exceso a años, si medimos alturas con una
precisión de centímetros, redondearemos por exceso a centímetros, etc. En el caso improbable
de que el cociente del rango entre el número de clases dé un valor exacto en la precisión de
la medida, tomaremos como A este cociente más una unidad de precisión; así, por ejemplo, si
hemos medido unas alturas en metros con una precisión de centímetros y obtenemos que el
cociente del rango entre k da un número exacto de centímetros, tomaremos como amplitud A
este cociente más 1 cm.
2
Pero no es la única forma posible de hacerlo, naturalmente. Recordad, por ejemplo, el agrupamiento de
las calificaciones en Suspenso, Aprobado, Notable y Sobresaliente, que representan intervalos de notas de
amplitudes diferentes.
11-4
Ejemplo 11.2. Seguimos con el Ejemplo 11.1; vamos a continuar el proceso de agrupamiento
de los datos en k = 7 clases. Recordemos que el rango del conjunto de datos en cuestión es
12.7 y que los datos están expresados en minutos con una precisión de una cifra decimal; por lo
tanto, la amplitud será el cociente 12.7/7 = 1.8143 redondeado por exceso a una cifra decimal:
A = 1.9.
Ahora hemos de calcular los extremos de los intervalos. En este curso, tomaremos estos
intervalos siempre cerrados a la izquierda y abiertos a la derecha, y los denotaremos por
[L1 , L2 ), [L2 , L3 ), . . . , [Lk , Lk+1 ).
Sin entrar en detalles, el motivo por el que tomamos los intervalos de esta forma y no al revés
(abiertos por la izquierda y cerrados por la derecha, que es como los construye R por defecto) es
porque así es como se usan en Teoría de Probabilidades al definir la distribución de una variable
aleatoria discreta, y también en muchas situaciones cotidianas (calificaciones, edades. . . ). En
todo caso, queremos haceros notar que, con la regla que explicamos a continuación, los extremos
de los intervalos nunca van a coincidir con valores del conjunto de datos: si la usáis, tanto dará
si consideráis los intervalos abiertos o cerrados en sus extremos.
Los extremos L1 , . . . , Lk+1 de estos intervalos se calculan de la manera siguiente: tomamos
como extremo izquierdo L1 del primer intervalo el valor
1
L1 = mín(x) · precisión;
2
es decir, si la precisión son las unidades en las que hemos medido los datos, L1 = mín(x) 0.5;
si la precisión son décimas de unidad, L1 = mín(x) 0.05; etc. A partir de este extremo inferior,
cada uno de los extremos siguientes se obtiene sumando la amplitud al anterior: L2 = L1 + A,
L3 = L2 +A y así sucesivamente, hasta llegar a Lk+1 = Lk +A. Por consiguiente, estos extremos
forman una progresión aritmética de paso A:
Li = L1 + (i 1)A, i = 2, . . . , k + 1.
Como decíamos, de esta manera se garantiza que los extremos de los intervalos nunca coincidan
con valores del conjunto de datos: por ejemplo, si los datos están expresados con una sola cifra
decimal, estos extremos tienen todos un 5 en su segunda cifra decimal.
Ejemplo 11.3. Continuemos con el Ejemplo 11.1 y k = 7; hemos visto que tenemos que tomar
A = 1.9. El valor mínimo del conjunto de datos es 3.8:
> min ( alergia )
[1] 3.8
Además, los datos están expresados con una precisión de décimas de unidad. El extremo inferior
del primer intervalo será, entonces, L1 = 3.8 0.05 = 3.75, y a partir de aquí obtendremos el
resto de extremos mediante una progresión aritmética de paso 1.9:
L1 = 3.75
L2 = 3.75 + 1.9 = 5.65
L3 = 3.75 + 2 · 1.9 = 7.55
L4 = 3.75 + 3 · 1.9 = 9.45
L5 = 3.75 + 4 · 1.9 = 11.35
L6 = 3.75 + 5 · 1.9 = 13.25
L7 = 3.75 + 6 · 1.9 = 15.15
L8 = 3.75 + 7 · 1.9 = 17.05
11-5
Los intervalos son, por lo tanto,
[3.75, 5.65), [5.65, 7.55), [7.55, 9.45), [9.45, 11.35), [11.35, 13.25),
[13.25, 15.15), [15.15, 17.05).
Finalmente, hemos de determinar la marca de clase Xi de cada intervalo [Li , Li+1 ); se trata de
un valor del intervalo que usaremos para identificar la clase y para calcular algunos estadísticos.
Como regla general, en este curso marcaremos el punto medio del intervalo,
Li + Li+1
Xi = ;
2
de esta manera, el error máximo que se comete al describir cualquier elemento del intervalo por
medio de su marca de clase es mínimo e igual a la mitad de la amplitud del intervalo.
Como todos los intervalos tienen la misma amplitud A, la diferencia entre dos puntos medios
consecutivos será también A, y por consiguiente las marcas de clase formarán de nuevo una
progresión aritmética de paso A:
L1 + L 2
X1 = y Xi = X1 + (i 1)A, i = 2, . . . , k.
2
Ejemplo 11.4. Continuemos con el Ejemplo 11.1 para k = 7. Las marcas de clase serán los
puntos medios de los intervalos que hemos determinado en el ejemplo anterior; como hemos
visto, formarán una progresión aritmética de origen el punto medio del primer intervalo y paso
la amplitud de las clases:
X1 = (3.75 + 5.65)/2 = 4.7
X2 = 4.7 + 1.9 = 6.6
X3 = 4.7 + 2 · 1.9 = 8.5
X4 = 4.7 + 3 · 1.9 = 10.4
X5 = 4.7 + 4 · 1.9 = 12.3
X6 = 4.7 + 5 · 1.9 = 14.2
X7 = 4.7 + 6 · 1.9 = 16.1
Ejemplo 11.5. Volvamos a la situación inicial del Ejemplo 11.1, y esta vez vamos a agrupar
los datos siguiendo la regla de Scott. Ya calculamos en su momento que, con esta regla, tenemos
que usar k = 5 intervalos. Como el rango de los datos es 12.7 y 12.7/5 = 2.54, redondeando
por exceso este cociente a una décima obtenemos que la amplitud de los intervalos ha de ser
A = 2.6.
Calculemos los extremos de los intervalos: el extremo inferior del primero es, de nuevo,
L1 = 3.8 0.05 = 3.75, y a partir de este valor, los otros extremos se obtienen sumando
consecutivamente la amplitud hasta llegar a L6 :
> L =3.75+2.6 * (0:5)
> L
[1] 3.75 6.35 8.95 11.55 14.15 16.75
También podríamos haber calculado estas marcas de clase definiéndolas directamente como
los puntos medios de los intervalos:
> X =( L [1:( length ( L ) - 1) ]+ L [2: length ( L ) ]) / 2
> X
[1] 5.05 7.65 10.25 12.85 15.45
Una vez agrupados los datos, ya podemos empezar a estudiarlos. Una primera posibilidad
es considerar las clases como los niveles de una variable ordinal y calcular sus frecuencias; así,
la frecuencia absoluta de una clase será el número de datos originales que pertenecen a esta
clase, la frecuencia absoluta acumulada de una clase será el número de datos originales que
pertenecen a esta clase o a alguna de las anteriores, etc. La manera usual de representar las
frecuencias de un conjunto de datos agrupados es la mostrada en la Tabla 11.1, donde Xj indica
la marca de clase, nj la frecuencia absoluta de la clase, Nj su frecuencia absoluta acumulada,
fj su frecuencia relativa y Fj su frecuencia relativa acumulada. Recordad que Nk será igual al
número total de datos recogidos y Fk siempre valdrá 1.
intervalos Xj nj Nj fj Fj
[L1 , L2 ) X1 n1 N1 f1 F1
[L2 , L3 ) X2 n2 N2 f2 F2
.. .. .. .. .. ..
. . . . . .
[Lk , Lk+1 ) Xk nk Nk fk Fk
Tabla 11.1. Modelo de tabla de frecuencias para datos agru-
pados.
Ejemplo 11.6. Continuemos con el Ejemplo 11.1; recordemos que los datos son
10.5, 11.2, 9.9, 15.0, 11.4, 12.7, 16.5, 10.1, 12.7, 11.4, 11.6, 6.2, 7.9, 8.3, 10.9, 8.1, 3.8, 10.5,
11.7, 8.4, 12.5, 11.2, 9.1, 10.4, 9.1, 13.4, 12.3, 5.9, 11.4, 8.8, 7.4, 8.6, 13.6, 14.7, 11.5, 11.5,
10.9, 9.8, 12.9, 9.9.
Las frecuencias de este conjunto de datos para su agrupamiento en 7 clases se muestran en
la Tabla 11.2. Para construir esta tabla, primero hemos calculado las frecuencias absolutas de
11-7
cada clase: sólo hay 1 valor dentro de [3.75, 5.65), por lo que n1 = 1; hay 3 valores dentro de
[5.65, 7.55), por lo que n2 = 3; etc. A partir de estas frecuencias absolutas, hemos calculado el
resto de la manera usual.
intervalos Xj nj Nj fj Fj
[3.75, 5.65) 4.7 1 1 0.025 0.025
[5.65, 7.55) 6.6 3 4 0.075 0.100
[7.55, 9.45) 8.5 8 12 0.200 0.300
[9.45, 11.35) 10.4 11 23 0.275 0.575
[11.35, 13.25) 12.3 12 35 0.300 0.875
[13.25, 15.15) 14.2 4 39 0.100 0.975
[15.15, 17.05) 16.1 1 40 0.025 1.000
Tabla 11.2. Frecuencias de los datos del Ejemplo 11.1 agrupa-
dos en 7 clases.
Ejemplo 11.7. Supongamos que los siguientes valores son números de árboles frutales afectados
por la mosca de la fruta en 50 terrenos rústicos de las mismas dimensiones:
8, 11, 11, 8, 9, 10, 16, 6, 12, 19, 13, 6, 9, 13, 15, 9, 12, 16, 8, 7, 14, 11, 15, 6, 14, 14, 17, 11, 6,
9, 10, 19, 12, 11, 12, 6, 15, 16, 16, 12, 13, 12, 12, 8, 17, 13, 7, 12, 14, 12.
Por consiguiente, usaremos 5 clases. Como hemos dado las medidas con una precisión de
unidades, para calcular su amplitud A hemos de redondear por exceso a unidades el cociente
entre el rango de la variable y el número de clases; este cociente vale 2.6, y por lo tanto A = 3.4
El extremo inferior de la primera clase será 6 0.5 = 5.5, y a partir de aquí calculamos los 5
extremos siguientes sumando sucesivamente la amplitud:
> 5.5+3 * (0:5)
[1] 5.5 8.5 11.5 14.5 17.5 20.5
3
Si os preguntáis por qué, la respuesta es. . . ¿por qué no? Ya lo hemos dicho, no hay una regla mejor que las
otras. En todo caso, se puede comprobar que esta regla suele dar un mayor número de clases.
4
Recordad que, si la precisión hubiera sido de décimas de unidad, como este cociente ha dado exactamente
2.6, hubiéramos tenido que tomar como amplitud el cociente más una unidad de precisión: 2.7.
11-8
Por consiguiente, los intervalos son
[5.5, 8.5), [8.5, 11.5), [11.5, 14.5), [14.5, 17.5), [17.5, 20.5).
Las marcas de clase serán los puntos medios de estos intervalos, que calcularemos sumando
múltiplos consecutivos de la amplitud al primer punto medio:
> (5.5+8.5) / 2+3 * (0:4)
[1] 7 10 13 16 19
Es decir,
X1 = 7, X2 = 10, X3 = 13, X4 = 16, X5 = 19.
Finalmente, contamos cuántos datos pertenecen a cada clase (serán las frecuencias absolutas)
y a partir de aquí calculamos el resto de frecuencias y obtenemos la Tabla 11.3. Lo que nos
interesa ahora es que R calcule esta tabla por nosotros.
intervalos Xj nj Nj fj Fj
[5.5, 8.5) 7 11 11 0.22 0.22
[8.5, 11.5) 10 11 22 0.22 0.44
[11.5, 14.5) 13 17 39 0.34 0.78
[14.5, 17.5) 16 9 48 0.18 0.96
[17.5, 20.5) 19 2 50 0.04 1.00
Tabla 11.3. Frecuencias de los datos del Ejemplo 11.7 agrupa-
dos según la regla de Freedman-Diaconis.
Las etiquetas que se usan para identificar estos intervalos suelen ser las siguientes:
Codificación 1. Los intervalos mismos: [0.5, 3.5), [3.5, 6.5), [6.5, 9.5).
Codificación 2. Las marcas de clase, que para cada intervalo sería su punto medio: 2, 5, 8.
Naturalmente, en la práctica podemos usar cualquier otra codificación que se nos ocurra.
La función básica de R para agrupar un vector de datos numéricos y codificar sus valores con
las clases a las que pertenecen es
11-9
cut(x, breaks=..., labels=..., right=...),
donde:
x es el vector numérico.
El parámetro breaks puede ser un vector numérico formado por los extremos de los
intervalos en los que queremos agrupar los datos y que habremos calculado previamente.
También podemos igualar este parámetro a un número k, en cuyo caso R agrupa los datos
en k clases; para ello, divide el intervalo comprendido entre los valores mínimo y máximo
de x en k intervalos y, a continuación, desplaza ligeramente a la izquierda el extremo
inferior del primero y a la derecha el extremo superior del último.5
El parámetro labels es un vector con las etiquetas de los intervalos. Su valor por defecto,
el que utiliza si no lo especificamos, es la codificación 1: usa como etiquetas los mismos
intervalos.6 Si especificamos labels=FALSE, obtenemos la codificación 3: los intervalos se
identifican por medio de números naturales correlativos empezando por 1. Para usar como
etiquetas las marcas de clase o cualquier otro vector, hay que entrarlo como valor de este
parámetro.
El parámetro right es un parámetro lógico que permite indicar qué tipo de intervalos
queremos. Si usamos intervalos cerrados por la izquierda y abiertos por la derecha, tenemos
que especificar right=FALSE, que no es el valor por defecto.
Hay otro parámetro que a veces es útil, include.lowest. Combinado con right=FALSE,
include.lowest=TRUE impone que el último intervalo sea cerrado: [Lk , Lk+1 ]. Usualmente
lo tomaremos abierto a la derecha, que es el efecto global de right=FALSE; pero en algunos
agrupamientos ad hoc puede que Lk+1 coincida con el máximo de la variable numérica y
entonces es necesario usar include.lowest=TRUE para no excluirlo: véase el Ejemplo 11.9
más adelante.
Podéis consultar la Ayuda de la función para conocer otros parámetros que os puedan ser de
utilidad y para saber cómo se pueden especificar otros tipos de intervalos.
10, 9, 8, 7, 3, 5, 6, 8, 9, 5, 2, 1, 3, 1, 1.
5
Por consiguiente, estos intervalos no tienen todos la misma amplitud, y además puede pasar que algún extremo
intermedio coincida con algún dato del conjunto.
6
Aunque puede que escriba sus extremos redondeados, para que muestren todos un número máximo de cifras
significativas: por defecto, 3.
11-10
> # Definimos un vector L con los extremos de los intervalos
> L =0.5+4 * (0:3)
> # Definimos x _ int como el resultado de la codificaci ó n en
intervalos empleando como etiquetas los intervalos
> x _ int = cut (x , breaks =L , right = FALSE )
> x _ int
[1] [8.5 ,12.5) [8.5 ,12.5) [4.5 ,8.5) [4.5 ,8.5) [0.5 ,4.5)
[6] [4.5 ,8.5) [4.5 ,8.5) [4.5 ,8.5) [8.5 ,12.5) [4.5 ,8.5)
[11] [0.5 ,4.5) [0.5 ,4.5) [0.5 ,4.5) [0.5 ,4.5) [0.5 ,4.5)
Levels : [0.5 ,4.5) [4.5 ,8.5) [8.5 ,12.5)
> # Definimos x _ MC como el resultado de la codificaci ó n en
intervalos empleando como etiquetas las marcas de clase
> MC =( L [1]+ L [2]) / 2+4 * (0:2) # Las marcas de clase
> x _ MC = cut (x , breaks =L , labels = MC , right = FALSE )
> x _ MC
[1] 10.5 10.5 6.5 6.5 2.5 6.5 6.5 6.5 10.5 6.5 2.5 2.5
[13] 2.5 2.5 2.5
Levels : 2.5 6.5 10.5
> # Definimos x _ Num como el resultado de la codificaci ó n en
intervalos empleando como etiquetas 1 , 2 , 3
> x _ Num = cut (x , breaks =L , labels = FALSE , right = FALSE )
> x _ Num
[1] 3 3 2 2 1 2 2 2 3 2 1 1 1 1 1
El resultado de cut ha sido, en cada caso, una lista con los elementos del vector original
codificados con las etiquetas de las clases a las que pertenecen. Podemos observar que las dos
primeras aplicaciones de cut han producido factores (cuyos niveles son los intervalos y las
marcas de clase, respectivamente, en ambos casos ordenados de manera natural), mientras que
aplicándolo con labels=FALSE hemos obtenido un vector.
Antes de continuar, ¿que habría pasado si hubiéramos pedido a R que cortase los datos en 3
grupos?
> x
[1] 10 9 8 7 3 5 6 8 9 5 2 1 3 1 1
> cut (x , breaks =3 , right = FALSE )
[1] [7 ,10) [7 ,10) [7 ,10) [4 ,7) [0.991 ,4) [4 ,7)
[7] [4 ,7) [7 ,10) [7 ,10) [4 ,7) [0.991 ,4) [0.991 ,4)
[13] [0.991 ,4) [0.991 ,4) [0.991 ,4)
Levels : [0.991 ,4) [4 ,7) [7 ,10)
Una vez agrupados los datos y codificados con las etiquetas de las clases, ya podemos calcular
las tablas de frecuencias absolutas, relativas y acumuladas de los datos agrupados. Una posibi-
lidad es usar las funciones table, prop.table y cumsum tal como lo hacíamos en las Lecciones
7 y 8. Otra posibilidad es usar la función hist, a la que dedicaremos la Sección 11.4. Esta
11-11
función sirve para dibujar el histograma de la variable cuantitativa agrupada (una especie de
diagrama de barras para las clases del agrupamiento), pero internamente da lugar a una list
cuya componente count es el vector de frecuencias absolutas de las clases. Por consiguiente,
para calcular estas frecuencias absolutas, podemos usar la instrucción
hist(x, breaks=..., right=FALSE, plot=FALSE)$count.
En esta instrucción, es conveniente igualar el parámetro breaks al vector de los extremos
de los intervalos (porque cut y hist usan métodos diferentes para agrupar los datos cuan-
do se especifica sólo el número de clases); el significado de right=FALSE (y, si es necesario,
include.lowest=TRUE) es el mismo que en cut; y plot=FALSE impide que se dibuje el histo-
grama. Por ahora es interesante también saber que el resultado de hist incluye la componente
mids que contiene el vector de puntos medios de los intervalos, nuestras marcas de clase.
Ejemplo 11.9. Supongamos que tenemos las 50 calificaciones siguientes, obtenidas por los
estudiantes de una asignatura:
5.1, 1.1, 6.4,5.3, 10, 5.4, 1.9, 3.1, 5.1, 0.8, 9.6, 6.6, 7.0, 9.6, 10, 1.2, 4.2, 8.8, 2.4, 1.8,
5.6, 6.8, 6.7, 2.2, 8.6, 3.9, 5.6, 5.9, 8.4, 4.9, 0.7, 8.2, 3.7, 4.8, 5.8, 3.3, 9.7, 7.8, 9.3,
4.5, 6.2, 3.9, 4.7, 6.2, 6.3, 9.4, 9.3, 2.3, 8.5, 1.4.
Vamos a agruparlas en Suspenso, Aprobado, Notable, y Sobresaliente, y calcularemos las fre-
cuencias de estas clases. Observad que las clases no tienen la misma amplitud, y que además
la última ha de ser cerrada a la derecha (ha de contener los dieces), por lo que tendremos que
usar include.lowest=TRUE.
> Notas = c (5.1 ,1.1 ,6.4 ,5.3 ,10 ,5.4 ,1.9 ,3.1 ,5.1 ,0.8 ,9.6 ,6.6 ,7.0 ,9.6 ,
10 ,1.2 ,4.2 ,8.8 ,2.4 ,1.8 ,5.6 ,6.8 ,6.7 ,2.2 ,8.6 ,3.9 ,5.6 ,5.9 ,8.4 ,4.9 ,
0.7 ,8.2 ,3.7 ,4.8 ,5.8 ,3.3 ,9.7 ,7.8 ,9.3 ,4.5 ,6.2 ,3.9 ,4.7 ,6.2 ,6.3 ,9.4 ,
9.3 ,2.3 ,8.5 ,1.4)
> Notas _ cut = cut ( Notas , breaks = c (0 ,5 ,7 ,9 ,10) ,
labels = c ( " Suspenso " ," Aprobado " ," Notable " ," Sobresaliente " ) ,
right = FALSE , include . lowest = TRUE )
[1] Aprobado Suspenso Aprobado Aprobado
[5] Sobresaliente Aprobado Suspenso Suspenso
[9] Aprobado Suspenso Sobresaliente Aprobado
[13] Notable Sobresaliente Sobresaliente Suspenso
[17] Suspenso Notable Suspenso Suspenso
[21] Aprobado Aprobado Aprobado Suspenso
[25] Notable Suspenso Aprobado Aprobado
[29] Notable Suspenso Suspenso Notable
[33] Suspenso Suspenso Aprobado Suspenso
[37] Sobresaliente Notable Sobresaliente Suspenso
[41] Aprobado Suspenso Suspenso Aprobado
[45] Aprobado Sobresaliente Sobresaliente Suspenso
[49] Notable Suspenso
Levels : Suspenso Aprobado Notable Sobresaliente
> table ( Notas _ cut ) # Frecuencias absolutas
Notas _ cut
Suspenso Aprobado Notable Sobresaliente
20 15 7 8
> cumsum ( table ( Notas _ cut ) ) # Frecuencias absolutas acumuladas
Suspenso Aprobado Notable Sobresaliente
11-12
20 35 42 50
> prop . table ( table ( Notas _ cut ) ) # Frecuencias relativas
Notas _ cut
Suspenso Aprobado Notable Sobresaliente
0.40 0.30 0.14 0.16
> cumsum ( prop . table ( table ( Notas _ cut ) ) ) # Frecuencias rel . acum .
Suspenso Aprobado Notable Sobresaliente
0.40 0.70 0.84 1.00
También podríamos haber obtenido estas frecuencias usando la función hist para calcular el
vector de frecuencias absolutas y luego operando con este vector para obtener el resto:
> frec _ abs = hist ( Notas , breaks = c (0 ,5 ,7 ,9 ,10) , right = FALSE ,
include . lowest = TRUE , plot = FALSE ) $ count # Frecuencias absolutas
> frec _ abs
[1] 20 15 7 8
> cumsum ( frec _ abs ) # Frecuencias absolutas acumuladas
[1] 20 35 42 50
> frec _ abs / length ( Notas ) # Frecuencias relativas
[1] 0.40 0.30 0.14 0.16
> cumsum ( frec _ abs / length ( Notas ) ) # Frecuencias relativas acumuladas
[1] 0.40 0.70 0.84 1.00
Ahora podemos construir un data frame que contenga las frecuencias de estas calificaciones
con la estructura de la Tabla 11.1. Como ya explicamos en el Ejemplo 9.2, si calculamos las
frecuencias absolutas con table, no es conveniente usar el resultado como columna del data
frame: es mejor utilizar el vector que se obtiene al aplicar as.vector al resultado de table, y
así no se generan columnas espurias con los nombres de los niveles.
> intervalos = c ( " [0 ,5) " ," [5 ,7) " ," [7 ,9) " ," [9 ,10] " )
> calificaciones = c ( " Suspenso " ," Aprobado " ," Notable " ," Sobresaliente " )
> marcas = c (2.5 ,6 ,8 ,9.5) # Marcas de clase
> f . abs = as . vector ( table ( Notas _ cut ) ) # Frecuencias absolutas
> f . abs . cum = cumsum ( f . abs ) # Frecuencias absolutas acumuladas
> f . rel = f . abs / length ( Notas ) # Frecuencias relativas
> f . rel . cum = cumsum ( f . rel ) # Frecuencias relativas acumuladas
> tabla . frec = data . frame ( intervalos , calificaciones , marcas , f . abs ,
f . abs . cum , f . rel , f . rel . cum ) # Construimos el data frame
> tabla . frec
intervalos calificaciones marcas f . abs f . abs . cum f . rel f . rel . cum
1 [0 ,5) Suspenso 2.5 20 20 0.40 0.40
2 [5 ,7) Aprobado 6.0 15 35 0.30 0.70
3 [7 ,9) Notable 8.0 7 42 0.14 0.84
4 [9 ,10] Sobresaliente 9.5 8 50 0.16 1.00
11-13
y usar el vector f.abs como arranque para calcular las columnas de frecuencias del data frame
y el vector marcas como columna de marcas de clase.
Ejemplo 11.10. Continuemos con el Ejemplo 11.8; vamos a calcular las diferentes frecuencias
para la codificación x_int:
> table ( x _ int )
x _ int
[0.5 ,4.5) [4.5 ,8.5) [8.5 ,12.5)
6 6 3
> prop . table ( table ( x _ int ) )
x _ int
[0.5 ,4.5) [4.5 ,8.5) [8.5 ,12.5)
0.4 0.4 0.2
> cumsum ( table ( x _ int ) )
[0.5 ,4.5) [4.5 ,8.5) [8.5 ,12.5)
6 12 15
> cumsum ( prop . table ( table ( x _ int ) ) )
[0.5 ,4.5) [4.5 ,8.5) [8.5 ,12.5)
0.4 0.8 1.0
Ahora, vamos a construir un data frame que contenga la tabla de frecuencias de esta variable
agrupada:
> intervalos = levels ( x _ int )
> marcas = MC # Las hemos calculado en el Ejemplo 11.8
> f . abs = as . vector ( table ( x _ int ) )
> f . abs . cum = cumsum ( f . abs )
> f . rel = f . abs / length ( x )
> f . rel . cum = cumsum ( f . rel )
> tabla . frec = data . frame ( intervalos , marcas , f . abs , f . abs . cum ,
f . rel , f . rel . cum )
> tabla . frec
intervalos marcas f . abs f . abs . cum f . rel f . rel . cum
1 [0.5 ,4.5) 2.5 6 6 0.4 0.4
2 [4.5 ,8.5) 6.5 6 12 0.4 0.8
3 [8.5 ,12.5) 10.5 3 15 0.2 1.0
Podemos automatizar el cálculo de esta tabla de frecuencias, usando las dos funciones siguien-
tes. La primera sirve en el caso en que vayamos a tomar todas las clases de la misma amplitud;
sus parámetros son: x, el vector con los datos; k, el número de clases; A, su amplitud; y p, la
precisión de los datos (p = 1 si la precisión son unidades, p = 0.1 si la precisión son décimas de
unidad, etc.).
> Tabla _ frec _ agrup = function (x ,k ,A , p ) {
L = min ( x ) - p / 2+ A * (0: k )
x _ int = cut (x , breaks =L , right = FALSE )
intervalos = levels ( x _ int )
marcas =( L [1]+ L [2]) / 2+ A * (0:( k - 1) )
f . abs = as . vector ( table ( x _ int ) )
f . rel = f . abs / length ( x )
11-14
f . abs . cum = cumsum ( f . abs )
f . rel . cum = cumsum ( f . rel )
tabla _ x = data . frame ( intervalos , marcas , f . abs , f . abs . cum , f . rel ,
f . rel . cum )
tabla _ x
}
Si de las clases conocemos de entrada sus extremos, podemos usar la función siguiente; sus
parámetros son: x, el vector con los datos; L, el vector de extremos de clases; y V , un valor
lógico, que ha de ser TRUE si queremos que el último intervalo sea cerrado, y FALSE en caso
contrario. Impondremos además que el valor por defecto de V sea FALSE. Para ello, especificamos
el parámetro mediante V=FALSE dentro de los paréntesis de function.
> Tabla _ frec _ agrup _ L = function (x ,L , V = FALSE ) {
x _ int = cut (x , breaks =L , right = FALSE , include . lowest = V )
intervalos = levels ( x _ int )
marcas =( L [1:( length ( L ) - 1) ]+ L [2: length ( L ) ]) / 2
f . abs = as . vector ( table ( x _ int ) )
f . rel = f . abs / length ( x )
f . abs . cum = cumsum ( f . abs )
f . rel . cum = cumsum ( f . rel )
tabla _ x = data . frame ( intervalos , marcas , f . abs , f . abs . cum , f . rel ,
f . rel . cum )
tabla _ x
}
Observad que, como advertíamos en su momento, ha escrito los extremos de los intervalos
redondeados para que tengan como máximo 3 cifras. Esto se puede resolver usando en la función
cut el parámetro dig.lab, que permite especificar el número máximo de cifras significativas
en los extremos de las etiquetas.
> L =3.75+1.9 * (0:7)
> L
11-15
[1] 3.75 5.65 7.55 9.45 11.35 13.25 15.15 17.05
> cut ( alergia , breaks =L , right = FALSE )
[1] [9.45 ,11.3) [9.45 ,11.3) [9.45 ,11.3) [13.2 ,15.1) [11.3 ,13.2)
[6] [11.3 ,13.2) [15.1 ,17) [9.45 ,11.3) [11.3 ,13.2) [11.3 ,13.2)
[11] [11.3 ,13.2) [5.65 ,7.55) [7.55 ,9.45) [7.55 ,9.45) [9.45 ,11.3)
[16] [7.55 ,9.45) [3.75 ,5.65) [9.45 ,11.3) [11.3 ,13.2) [7.55 ,9.45)
[21] [11.3 ,13.2) [9.45 ,11.3) [7.55 ,9.45) [9.45 ,11.3) [7.55 ,9.45)
[26] [13.2 ,15.1) [11.3 ,13.2) [5.65 ,7.55) [11.3 ,13.2) [7.55 ,9.45)
[31] [5.65 ,7.55) [7.55 ,9.45) [13.2 ,15.1) [13.2 ,15.1) [11.3 ,13.2)
[36] [11.3 ,13.2) [9.45 ,11.3) [9.45 ,11.3) [11.3 ,13.2) [9.45 ,11.3)
7 Levels : [3.75 ,5.65) [5.65 ,7.55) [7.55 ,9.45) ... [15.1 ,17)
> cut ( alergia , breaks =L , right = FALSE , dig . lab =4)
[1] [9.45 ,11.35) [9.45 ,11.35) [9.45 ,11.35) [13.25 ,15.15)
[5] [11.35 ,13.25) [11.35 ,13.25) [15.15 ,17.05) [9.45 ,11.35)
[9] [11.35 ,13.25) [11.35 ,13.25) [11.35 ,13.25) [5.65 ,7.55)
[13] [7.55 ,9.45) [7.55 ,9.45) [9.45 ,11.35) [7.55 ,9.45)
[17] [3.75 ,5.65) [9.45 ,11.35) [11.35 ,13.25) [7.55 ,9.45)
[21] [11.35 ,13.25) [9.45 ,11.35) [7.55 ,9.45) [9.45 ,11.35)
[25] [7.55 ,9.45) [13.25 ,15.15) [11.35 ,13.25) [5.65 ,7.55)
[29] [11.35 ,13.25) [7.55 ,9.45) [5.65 ,7.55) [7.55 ,9.45)
[33] [13.25 ,15.15) [13.25 ,15.15) [11.35 ,13.25) [11.35 ,13.25)
[37] [9.45 ,11.35) [9.45 ,11.35) [11.35 ,13.25) [9.45 ,11.35)
7 Levels : [3.75 ,5.65) [5.65 ,7.55) [7.55 ,9.45) ... [15.15 ,17.05)
Por lo tanto, si quisiéramos permitir que los extremos de las etiquetas tuvieran más de 3
cifras significativas, bastaría redefinir la función Tabla_frec_agrup añadiéndole un parámetro
dig.lab, que tuviera valor por defecto 3, y que se entrara a la función cut.
> Tabla _ frec _ agrup = function (x ,k ,A ,p , dig . lab =3) {
L = min ( x ) - p / 2+ A * (0: k )
x _ int = cut (x , breaks =L , right = FALSE , dig . lab = dig . lab )
intervalos = levels ( x _ int )
marcas =( L [1]+ L [2]) / 2+ A * (0:( k - 1) )
f . abs = as . vector ( table ( x _ int ) )
f . rel = f . abs / length ( x )
f . abs . cum = cumsum ( f . abs )
f . rel . cum = cumsum ( f . rel )
tabla _ x = data . frame ( intervalos , marcas , f . abs , f . abs . cum , f . rel ,
f . rel . cum )
tabla _ x
}
Ejemplo 11.12. Vamos a calcular la tabla de frecuencias de los datos del Ejemplo 11.7 usando
la función Tabla_frec_agrup. Ya habíamos decidido que k = 5 y que en este caso la amplitud
era 3. Los datos estaban expresados en unidades. Obtendremos la Tabla 11.3.
> fruta = c (8 ,11 ,11 ,8 ,9 ,10 ,16 ,6 ,12 ,19 ,13 ,6 ,9 ,13 ,15 ,9 ,12 ,16 ,8 ,7 ,14 ,
11 ,15 ,6 ,14 ,14 ,17 ,11 ,6 ,9 ,10 ,19 ,12 ,11 ,12 ,6 ,15 ,16 ,16 ,12 ,13 ,12 ,12 ,
8 ,17 ,13 ,7 ,12 ,14 ,12)
> Tabla _ frec _ agrup ( fruta , 5 , 3 , 1)
intervalos marcas f . abs f . abs . cum f . rel f . rel . cum
11-16
1 [5.5 ,8.5) 7 11 11 0.22 0.22
2 [8.5 ,11.5) 10 11 22 0.22 0.44
3 [11.5 ,14.5) 13 17 39 0.34 0.78
4 [14.5 ,17.5) 16 9 48 0.18 0.96
5 [17.5 ,20.5) 19 2 50 0.04 1.00
Ejemplo 11.13. Vamos a volver a calcular la tabla de frecuencias de las notas del Ejemplo 11.9,
usando esta vez una de nuestras funciones: como las clases tienen amplitudes diferentes y la
última es cerrada, usaremos la función Tabla_frec_agrup_L con V igual a TRUE.
> Notas = c (5.1 ,1.1 ,6.4 ,5.3 ,10 ,5.4 ,1.9 ,3.1 ,5.1 ,0.8 ,9.6 ,6.6 ,7.0 ,9.6 ,
10 ,1.2 ,4.2 ,8.8 ,2.4 ,1.8 ,5.6 ,6.8 ,6.7 ,2.2 ,8.6 ,3.9 ,5.6 ,5.9 ,8.4 ,4.9 ,
0.7 ,8.2 ,3.7 ,4.8 ,5.8 ,3.3 ,9.7 ,7.8 ,9.3 ,4.5 ,6.2 ,3.9 ,4.7 ,6.2 ,6.3 ,9.4 ,
9.3 ,2.3 ,8.5 ,1.4)
> Tabla _ frec _ agrup _ L ( Notas , c (0 ,5 ,7 ,9 ,10) , TRUE )
intervalos marcas f . abs f . abs . cum f . rel f . rel . cum
1 [0 ,5) 2.5 20 20 0.40 0.40
2 [5 ,7) 6.0 15 35 0.30 0.70
3 [7 ,9) 8.0 7 42 0.14 0.84
4 [9 ,10] 9.5 8 50 0.16 1.00
11-17
Este fichero contiene dos columnas: una con los grupos de edad y otra con las poblaciones.
Vamos a usar estos datos agrupados para calcular algunos estadísticos de la distribución de la
población española por edades en ese año.
Lo primero que hacemos es importarlo en un data frame; para ello podemos usar la función
read.csv o la función read.table con header=TRUE y sep=",". Como la variable de grupos
de edad tiene todos sus valores diferentes y no la usaremos para clasificar otros valores, la
importaremos como un vector de palabras con stringsAsFactors=FALSE. Como las etiquetas
de los grupos de edad contienen la palabra “años”, para garantizar que las eñes se importan de
manera correcta es conveniente incluir encoding="utf8".
> tabla = read . csv ( " http : / / aprender . uib . es / Rdir / cens81 . csv " ,
stringsAsFactors = FALSE , encoding = " utf8 " )
> str ( tabla )
’ data . frame ’: 18 obs . of 2 variables :
$ Edades : chr " De 0 a 4 a ñ os " " De 5 a 9 a ñ os " " De 10 a 14 a ñ os "
" De 15 a 19 a ñ os " ...
$ Poblaci ó n : int 3075352 3308049 3302328 3263312 2942178 2537428
2455314 2245806 2056009 2361225 ...
> tabla
Edades Poblaci ó n
1 De 0 a 4 a ñ os 3075352
2 De 5 a 9 a ñ os 3308049
3 De 10 a 14 a ñ os 3302328
4 De 15 a 19 a ñ os 3263312
5 De 20 a 24 a ñ os 2942178
6 De 25 a 29 a ñ os 2537428
7 De 30 a 34 a ñ os 2455314
8 De 35 a 39 a ñ os 2245806
9 De 40 a 44 a ñ os 2056009
10 De 45 a 49 a ñ os 2361225
11 De 50 a 54 a ñ os 2265091
12 De 55 a 59 a ñ os 2038002
13 De 60 a 64 a ñ os 1596543
14 De 65 a 69 a ñ os 1445606
15 De 70 a 74 a ñ os 1213807
16 De 75 a 79 a ñ os 852180
17 De 80 a 84 a ñ os 461960
18 De 85 y m á s a ñ os 263171
Hay que tener en cuenta que, en esta tabla, la clase «De 0 a 4 años» representa el interva-
lo de edades [0, 5), la clase «De 5 a 9 años» representa el intervalo de edades [5, 10), y así
sucesivamente.
Para calcular las diferentes medidas estadísticas, hemos de asignar a cada grupo de edades un
valor numérico como marca de clase: para los 17 primeros grupos, de amplitud 5, tomaremos
su edad media, y para el último, de amplitud indeterminada, tomaremos 90 como marca.
Añadiremos estas marcas al data frame anterior como una nueva variable.
> tabla $ marcas = c (2.5+5 * (0:16) ,90)
> head ( tabla )
Edades Poblaci ó n marcas
11-18
1 De 0 a 4 a ñ os 3075352 2.5
2 De 5 a 9 a ñ os 3308049 7.5
3 De 10 a 14 a ñ os 3302328 12.5
4 De 15 a 19 a ñ os 3263312 17.5
5 De 20 a 24 a ñ os 2942178 22.5
6 De 25 a 29 a ñ os 2537428 27.5
Por lo tanto, con los datos de los que disponemos, podemos afirmar que la edad media de los
españoles censados en 1981 era de unos 34 años, con una desviación típica de unos 22.5 años,
y que el grupo de edad más numeroso era el de los niños y niñas de 5 a 9 años.
Se han propuesto muchos métodos para aproximar la mediana y los otros cuantiles de una
variable cuantitativa agrupada a partir de las tablas de frecuencias de sus clases. Aquí ex-
plicaremos una de las más sencillas, y la ilustraremos con el ejemplo anterior; para empezar,
vamos a completar el data frame con las frecuencias absolutas acumuladas, relativas y relativas
acumuladas:
> tabla $ FA . acum = cumsum ( tabla $ Poblaci ó n )
> tabla $ FR = round ( tabla $ Poblaci ó n / Total , 3)
> tabla $ FR . acum = round ( tabla $ FA . acum / Total , 3)
> tabla
Edades Poblaci ó n marcas FA . acum FR FR . acum
1 De 0 a 4 a ñ os 3075352 2.5 3075352 0.082 0.082
2 De 5 a 9 a ñ os 3308049 7.5 6383401 0.088 0.169
3 De 10 a 14 a ñ os 3302328 12.5 9685729 0.088 0.257
4 De 15 a 19 a ñ os 3263312 17.5 12949041 0.087 0.344
5 De 20 a 24 a ñ os 2942178 22.5 15891219 0.078 0.422
6 De 25 a 29 a ñ os 2537428 27.5 18428647 0.067 0.489
7 De 30 a 34 a ñ os 2455314 32.5 20883961 0.065 0.554
8 De 35 a 39 a ñ os 2245806 37.5 23129767 0.060 0.614
9 De 40 a 44 a ñ os 2056009 42.5 25185776 0.055 0.668
11-19
10 De 45 a 49 a ñ os 2361225 47.5 27547001 0.063 0.731
11 De 50 a 54 a ñ os 2265091 52.5 29812092 0.060 0.791
12 De 55 a 59 a ñ os 2038002 57.5 31850094 0.054 0.845
13 De 60 a 64 a ñ os 1596543 62.5 33446637 0.042 0.888
14 De 65 a 69 a ñ os 1445606 67.5 34892243 0.038 0.926
15 De 70 a 74 a ñ os 1213807 72.5 36106050 0.032 0.958
16 De 75 a 79 a ñ os 852180 77.5 36958230 0.023 0.981
17 De 80 a 84 a ñ os 461960 82.5 37420190 0.012 0.993
18 De 85 y m á s a ñ os 263171 90.0 37683361 0.007 1.000
Llamaremos intervalo crítico para la mediana al primer intervalo donde la frecuencia relativa
acumulada sea mayor o igual que 0.5. En este caso, el intervalo crítico es la clase «De 30 a 34
años», es decir, [30, 35).
Sean [Lc , Lc+1 ) este intervalo crítico; Nc 1 , la frecuencia absoluta acumulada del intervalo
anterior al crítico (si el intervalo crítico es el primero, tomamos Nc 1 = 0); nc , la frecuencia
absoluta del intervalo crítico; Ac = Lc+1 Lc , su amplitud; y n, el número total de datos.
Entonces, la fórmula siguiente nos da una aproximación M para la mediana de los datos «reales»
a partir de los datos agrupados:
n
2
Nc 1
M = Lc + Ac · .
nc
La justificación de esta fórmula es la siguiente: lo que hacemos es unir con una recta las frecuen-
cias absolutas acumuladas en Lc y en Lc+1 , y aproximar la mediana por medio de la abscisa
del punto sobre esta recta cuya ordenada es n/2 (véase la Figura 11.2).
Nc ·············································································
⌘··
⌘ ·····
⌘ ····
⌘ ····
⌘
n/2 ············································· ⌘
···
···
···
⌘·· ····
⌘ ····
⌘ ···· ····
⌘ ·· ····
⌘ ·
··· ····
Nc 1 ···············⌘
··· ···· ····
···· ···· ····
···· ···· ····
·· ···· ····
··
Lc M Lc+1
11-20
datos agrupados con la fórmula siguiente:
p·n Nc 1
Q p = L c + Ac · ,
nc
donde ahora el intervalo crítico [Lc , Lc+1 ) es el primer intervalo con frecuencia relativa acumu-
lada mayor o igual que p y el resto de valores se definen relativos a este intervalo crítico. De
este modo, en nuestro ejemplo, el intervalo crítico para Q0.25 es «De 10 a 14 años», y en este
caso Lc = 10, Ac = 5, Nc 1 = 6383401 y nc = 3302328, por lo que
0.25 · 37683361 6383401
Q0.25 = 10 + 5 · = 14.59894.
3302328
En cuanto al tercer cuantil, Q0.75 , el intervalo crítico es «De 50 a 54 años», por lo que Lc = 50,
Ac = 5, Nc 1 = 27547001 y nc = 2265091, y, por consiguiente,
0.75 · 37683361 27547001
Q0.75 = 50 + 5 · = 51.57945.
2265091
11.4. Histogramas
Los datos agrupados se describen gráficamente por medio de unos diagramas de barras especí-
ficos llamados histogramas, donde se dibuja sobre cada clase una barra cuya área representa la
frecuencia de dicha clase. Veamos un ejemplo.
10, 9, 8, 1, 9, 8, 2, 5, 7, 3, 5, 6, 1, 3, 7, 8, 9, 8, 5, 6, 2, 4, 1, 3, 5, 4, 6, 7, 10, 8, 5, 4, 2, 7, 8
La Figura 11.3.(a) muestra un histograma de las frecuencias absolutas de estos datos con este
agrupamiento. Podéis comprobar que el producto de la base por la altura de cada barra es igual
a la frecuencia de la clase correspondiente, que hemos escrito dentro de la barra para facilitar
su lectura. Como todas las clases tienen la misma amplitud, las alturas de estas barras son
proporcionales a las frecuencias de sus clases (son estas frecuencias divididas por la amplitud)
y las representan correctamente, de forma que habríamos podido marcar sin ningún problema
las frecuencias sobre el eje vertical, como hemos hecho en la Figura 11.3.(b).
11-21
(a) (b)
Figura 11.3. Histogramas del Ejemplo 11.15: (a) Histograma
real; (b) Histograma con las frecuencias marcadas en el eje de
ordenadas.
Pero si las amplitudes de las clases no son iguales, las alturas de las barras en un histograma
no representan correctamente las frecuencias de las clases. A modo de ejemplo, supongamos
que los datos anteriores son notas y que las agrupamos en suspensos, aprobados, notables y
sobresalientes:
[0, 5), [5, 7), [7, 9), [9, 10].
Recordad que, en este caso, el último intervalo ha de ser cerrado.
> L2 = c (0 ,5 ,7 ,9 ,10)
> x _ int2 = cut (x , breaks = L2 , right = FALSE , include . lowest = TRUE )
> table ( x _ int2 )
x _ int2
[0 ,5) [5 ,7) [7 ,9) [9 ,10]
12 8 10 5
En la Figura 11.4 podéis ver un histograma de frecuencias absolutas de estos datos con este
agrupamiento. Comprobaréis que las alturas de las barras son las necesarias para que el área
de cada barra sea igual a la frecuencia de la clase correspondiente; como las bases son de
amplitudes diferentes, estas alturas no son proporcionales a las frecuencias de las clases, por lo
que no tiene sentido marcar las frecuencias en el eje vertical; ¡el 12 de la primera barra estaría
por debajo del 5 de la última!
También se usan histogramas para representar frecuencias acumuladas de datos agrupados; en
este caso, y a diferencia del anterior, las alturas representan las frecuencias independientemente
de la base. El motivo es que estas alturas tienen que ir creciendo. Así, los histogramas de
frecuencias absolutas acumuladas de nuestros datos para los dos agrupamientos anteriores serían
los mostrados en la Figura 11.5.
La Figura 11.6 muestra la estructura básica de dos histogramas, el izquierdo para las frecuen-
cias absolutas y el derecho para las frecuencias absolutas acumuladas. En un histograma, el eje
11-22
Figura 11.4. Histograma del Ejemplo 11.15 con clases de dife-
rentes amplitudes.
de las abscisas representa los datos, donde marcamos los extremos de las clases, y se dibuja una
barra sobre cada clase; esta barra tiene un significado diferente según el tipo de histograma,
pero en general representa la frecuencia de su clase:
11-23
Figura 11.5. Histogramas de frecuencias acumuladas de los
datos del Ejemplo 11.15 para dos agrupamientos diferentes.
o unir las clases vacías con alguna de sus adyacentes, aunque de esta última manera rompamos
la regla básica de usar clases de la misma amplitud.
La función para dibujar histogramas con R es hist. Su estructura básica es
hist(x, breaks=..., freq=..., right=..., ...),
donde:
11-24
Histograma de frecuencias absolutas
Frecuencias absolutas
n1 n2
nk ......
L1 L2 L3 Lk Lk+1
Intervalos
plot, que ha salido hace unas páginas: igualado a FALSE, calcula el histograma, pero no
lo dibuja.
Como podéis ver, hist ha dibujado los ejes y las barras, pero en el eje horizontal no ha
marcado los extremos de las clases; por lo que refiere al eje vertical, en el histograma con
las clases de las mismas amplitudes, ha marcado las frecuencias absolutas, pero en el otro ha
marcado las densidades, lo que dificulta su comprensión. Además, fijaos en los títulos: hist
titula por defecto los histogramas «Histogram of» seguido del nombre del vector de datos, lo
que no es muy adecuado si no estáis escribiendo en inglés.
Por suerte, el resultado de hist contiene mucha información escondida, que podemos usar
para mejorar estos histogramas.
> h = hist ( fruta , breaks = L1 , right = FALSE , plot = FALSE ) # Para que no lo
dibuje ; ser í a el histograma de la izquierda de la Fig . 10.7
11-25
Histogram of fruta Histogram of fruta
0.12
15
0.10
0.08
10
Frequency
Density
0.06
0.04
5
0.02
0.00
0
5 10 15 20 5 10 15 20
fruta fruta
> h
$ breaks
[1] 5.5 8.5 11.5 14.5 17.5 20.5
$ counts
[1] 11 11 17 9 2
$ density
[1] 0.07333333 0.07333333 0.11333333 0.06000000 0.01333333
$ mids
[1] 7 10 13 16 19
$ xname
[1] " fruta "
$ equidist
[1] TRUE
En concreto:
La componente mids contiene el vector de puntos medios de los intervalos (que usamos
como marcas de clase): X1 , X2 , . . . , Xk .
11-26
La componente density contiene el vector de las densidades de los intervalos. Estas
densidades son las alturas de las barras del histograma de frecuencias relativas; por lo
tanto, la densidad de cada intervalo es su frecuencia relativa dividida por su amplitud.
Podemos servirnos de toda esta información para mejorar el histograma producido por defecto.
Por ejemplo, para histogramas de frecuencias absolutas, podéis usar la función siguiente; sus
parámetros son: x, el vector de datos, y L, el vector de extremos de los intervalos.
> hist _ abs = function (x , L ) {
h = hist (x , breaks =L , right = FALSE , freq = FALSE ,
axes = FALSE , col = " lightgray " ,
main = " Histograma de frecuencias absolutas " ,
xlab = " Intervalos y marcas de clase " ,
ylab = " Frecuencias absolutas " )
axis (1 , at = L )
text ( h $ mids , h $ density / 2 , labels = h $ counts , col = " blue " )
}
17
11 11
9
Algunos comentarios sobre la manera como hemos definido esta función, para que la podáis
modificar:
11-27
La instrucción axis(i, at=...) dibuja el eje correspondiente al valor de i (i = 1, el de
abscisas; i = 2, el de ordenadas) con marcas en los lugares indicados por el vector definido
mediante at; por lo tanto, la instrucción axis(1, at=L) añade un eje de abscisas (que
no habíamos dibujado) con marcas en los extremos de las clases.
Naturalmente, podéis adaptar a vuestro gusto esta función hist_abs o las otras que daremos:
podéis cambiar el título, cambiar los colores, etc. A modo de ejemplo, si usáis muchas clases,
es probable que las barras queden muy estrechas y no tenga sentido escribir en su interior las
frecuencias absolutas. Si todas las clases son de la misma amplitud, podéis representar estas
frecuencias en el eje de ordenadas: para ello, en la definición de hist_abs basta con sustituir,
en la función hist, freq=FALSE por freq=TRUE y axes=FALSE por xaxt="n" (y así dibujará el
eje de ordenadas con marcas que representen frecuencias absolutas), y eliminar la instrucción
text.
> hist _ abs2 = function (x , L ) {
h = hist (x , breaks =L , right = FALSE , freq = TRUE ,
xaxt = " n " , col = " lightgray " ,
main = " Histograma de frecuencias absolutas " ,
xlab = " Intervalos y marcas de clase " ,
ylab = " Frecuencias absolutas " )
axis (1 , at = L )
}
Otra posibilidad para indicar las frecuencias absolutas de las barras es usar la función rug,
que permite añadir al histograma una «alfombra» con marcas en todos los valores del vector; el
grosor de cada marca es proporcional a la frecuencia del valor que representa. Así, por ejemplo,
> hist _ abs ( fruta , L1 )
> rug ( fruta )
11-28
Histograma de frecuencias absolutas Histograma de frecuencias absolutas
Frecuencias absolutas
Frecuencias absolutas
17 17
11 11 11 11
9 9
2 2
5.5 8.5 11.5 14.5 17.5 20.5 5.5 8.5 11.5 14.5 17.5 20.5
50
48
39
22
11
Intervalos
Con esta función producimos el histograma «básico» de los datos, sin dibujarlo, y a conti-
11-29
nuación modificamos su componente density para que contenga las sumas acumuladas de la
componente density del histograma original. Luego dibujamos el nuevo histograma resultante,
aplicándole la función plot; los parámetros del gráfico se tienen que añadir a este plot, no
al histograma original. Finalmente, completamos el gráfico añadiendo el eje de abscisas y las
frecuencias acumuladas en el interior de las barras.
Pasemos a los histogramas de frecuencias relativas. En ellos, es costumbre superponer una
curva que estime la densidad de la distribución de la variable definida por la característica que
medimos.
La densidad de una variable es una curva tal que el área comprendida entre el eje de abscisas
y la curva sobre un intervalo es igual a la fracción de individuos de la población que caen dentro
de ese intervalo. Visualmente, imaginemos que vamos aumentando el tamaño de la muestra y
que agrupamos los datos en una familia cada vez mayor de intervalos; si el rango de los datos se
mantiene más o menos constante, la amplitud de los intervalos del histograma irá decreciendo;
cuando el tamaño del conjunto de datos tiende a infinito, los intervalos tienden a ser puntos y,
las barras, a ser líneas verticales. Los extremos superiores de estas líneas dibujarán una curva:
ésta es la densidad de la variable.
La densidad más famosa es la llamada campana de Gauss, y corresponde a una variable que
tenga una distribución normal (véase la Figura 11.11). Hay muchas variables que suelen tener
distribuciones normales: características morfológicas y fisiológicas de individuos de una especie,
calificaciones en exámenes, errores de medida. . . En cada caso, la forma concreta de la campana
depende de dos parámetros: el valor medio µ de la variable y su desviación típica .
Hay muchos métodos para estimar la densidad de la distribución a partir de una muestra;
la manera más sencilla de hacerlo con R es usar la función density. Cuando aplicamos esta
función, con sus parámetros por defecto, a un vector númerico, produce una list que incluye
los vectores x e y de primeras y segundas coordenadas de una secuencia de puntos (x, y) sobre
la curva densidad estimada.8 Aplicando plot, con type="l", o lines (si hay que añadirla a un
gráfico anterior) al resultado de density, obtenemos el gráfico de esta curva.
> str ( density ( fruta ) )
List of 7
$ x : num [1:512] 1.69 1.73 1.78 1.82 1.86 ...
$ y : num [1:512] 0.000326 0.000356 0.000388 0.000424
0.000462 ...
$ bw : num 1.44
8
Explicar el método que usa esta función density para estimar la densidad cae fuera del nivel de este curso.
Los curiosos pueden consultar el artículo de la Wikipedia http://en.wikipedia.org/wiki/Kernel_density_
estimation y luego la Ayuda de la función.
11-30
$ n : int 50
$ call : language density . default ( x = fruta )
$ data . name : chr " fruta "
$ has . na : logi FALSE
- attr ( * , " class " ) = chr " density "
> plot ( density ( fruta ) , type = " l " , xlab = " N ú mero de á rboles " ,
ylab = " Densidad " , main = " Densidad de la variable \ " fruta \ " " )
0.04
0.02
0.00
5 10 15 20
Número de árboles
Para dibujar un histograma de frecuencias relativas más informativo que el que produce R
por defecto y que incluya la estimación de la densidad, podéis usar la función siguiente:
> hist _ rel = function (x , L ) {
h = hist (x , breaks =L , right = FALSE , plot = FALSE )
t = round (1.1 * max ( max ( density ( x ) [[2]]) ,h $ density ) ,2)
plot (h , freq = FALSE , col = " lightgray " ,
main = " Histograma de frec . relativas y curva de densidad estimada " ,
xaxt = " n " , ylim = c (0 , t ) , xlab = " Intervalos " , ylab = " Densidades " )
axis (1 , at = L )
text ( h $ mids , h $ density / 2 ,
labels = round ( h $ counts / length ( x ) ,2) , col = " blue " )
lines ( density ( x ) , col = " red " , lwd =2)
}
11-31
Histograma de frec. relativas y curva de densidad estimada
0.12
0.10
0.08
Densidades
0.06
0.34
0.04
0.22 0.22
0.18
0.02
0.04
0.00
Intervalos
función de distribución de una variable nos da, en cada punto, la fracción de individuos de la
población que caen a la izquierda de este punto, es decir, la frecuencia relativa acumulada por
la variable sobre la población en ese punto. En general, la función de distribución en un valor
determinado es igual al área de la región, a la izquierda de la vertical definida por dicho valor,
delimitada por la función de densidad y el eje de abscisas.
Para dibujar un histograma de frecuencias relativas acumuladas que incluya la función de
distribución estimada, podéis usar la función siguiente:
> hist _ rel . cum = function (x , L ) {
h = hist (x , breaks =L , right = FALSE , plot = FALSE )
h $ density = cumsum ( h $ counts ) / length ( x ) # calculamos las f . relativas
plot (h , freq = FALSE , main = " Histograma de frec . rel . acumuladas \ n y
curva de distribuci ó n estimada " , xaxt = " n " , col = " lightgray " ,
xlab = " Intervalos " , ylab = " Frec . relativas acumuladas " )
axis (1 , at = L )
text ( h $ mids , h $ density / 2 ,
labels = round ( h $ density ,2) , col = " blue " )
dens . x = density ( x )
dens . x $ y = cumsum ( dens . x $ y ) * ( dens . x $ x [2] - dens . x $ x [1])
lines ( dens .x , col = " red " , lwd =2)
}
Ejemplo 11.17. Consideremos los datos de tiempos de inicio de reacción alérgica a una pica-
dura del Ejemplo 11.1. Queremos dibujar los histogramas de frecuencias relativas y relativas
11-32
Histograma de frec. rel. acumuladas
y curva de distribución estimada
1.0
0.8
Frec. relativas acumuladas
0.6
1
0.96
0.4
0.78
0.44
0.2
0.22
0.0
Intervalos
Ejemplo 11.18. El fichero datacrab.txt, del cual hemos guardado una copia en
http://aprender.uib.es/Rdir/datacrab.txt
recoge los datos sobre hembras de límula del Atlántico analizadas en el artículo «Satellite
male groups in horseshoe crabs, Limulus polyphemus» de H. J. Brockmann (Ethology 102
(1996), pp. 1–21). Una de las variables que incluye esta tabla de datos es la amplitud, width,
de los especímenes analizados. Vamos a dibujar un histograma de las frecuencias relativas de
11-33
Histograma de frec. rel. acumuladas
Histograma de frec. relativas y curva de densidad estimada
y curva de distribución estimada
1.0
0.8
0.15
0.6
Densidades
0.10
0.98 1
0.88
0.3
0.4
0.28
0.57
0.05
0.2
0.2
0.1 0.3
0.08
0.02 0.02 0.1
0.00
0.02
0.0
3.75 5.65 7.55 9.45 11.35 13.25 15.15 17.05 3.75 5.65 7.55 9.45 11.35 13.25 15.15 17.05
Intervalos Intervalos
estas amplitudes que incluya su curva de densidad estimada; para variar, agruparemos estas
amplitudes usando la regla de Scott. Aprovecharemos para calcular la tabla de frecuencias de
este agrupamiento.
Empezamos importando esta tabla en un data frame y definiendo un vector con la variable
correspondiente a la amplitud.
> crab = read . table ( " http : / / aprender . uib . es / Rdir / datacrab . txt " ,
header = TRUE )
> str ( crab )
’ data . frame ’: 173 obs . of 5 variables :
$ input : int 3 4 2 4 4 3 2 4 3 4 ...
$ color . spine : int 3 3 1 3 3 3 1 2 1 3 ...
$ width : num 28.3 22.5 26 24.8 26 23.8 26.5 24.7 23.7 25.6
...
$ satell : int 8 0 9 0 4 0 0 0 0 0 ...
$ weight : int 3050 1550 2300 2100 2600 2100 2350 1900 1950
2150 ...
> crw = crab $ width
Observamos que las amplitudes están expresadas con una precisión de décimas de unidad. A
continuación, determinamos el número de clases, amplitud, etc. del agrupamiento de este vector
crw siguiendo la regla de Scott.
> nclass . scott ( crw )
[1] 10
> diff ( range ( crw ) ) / 10
[1] 1.25
> # Tomaremos A =1.3
> L . cr = min ( crw ) - 0.05+1.3 * (0:10)
> L . cr
[1] 20.95 22.25 23.55 24.85 26.15 27.45 28.75 30.05 31.35 32.65
[11] 33.95
11-34
> MC . cr =( L . cr [1]+ L . cr [2]) / 2+1.3 * (0:9)
> MC . cr
[1] 21.6 22.9 24.2 25.5 26.8 28.1 29.4 30.7 32.0 33.3
> crw _ int = cut ( crw , breaks = L . cr , right = FALSE )
Ahora calcularemos la tabla de frecuencias para este agrupamiento crw_int. Usaremos la fun-
ción Tabla_frec_agrup de la página 11-14.
> Tabla _ frec _ agrup ( crw ,10 ,1.3 ,0.1 , dig . lab =4)
intervalos marcas f . abs f . abs . cum f . rel f . rel . cum
1 [20.9 ,22.2) 21.6 2 2 0.011560694 0.01156069
2 [22.2 ,23.6) 22.9 14 16 0.080924855 0.09248555
3 [23.6 ,24.9) 24.2 27 43 0.156069364 0.24855491
4 [24.9 ,26.1) 25.5 44 87 0.254335260 0.50289017
5 [26.1 ,27.4) 26.8 34 121 0.196531792 0.69942197
6 [27.4 ,28.8) 28.1 31 152 0.179190751 0.87861272
7 [28.8 ,30) 29.4 15 167 0.086705202 0.96531792
8 [30 ,31.4) 30.7 3 170 0.017341040 0.98265896
9 [31.4 ,32.6) 32.0 2 172 0.011560694 0.99421965
10 [32.6 ,34) 33.3 1 173 0.005780347 1.00000000
0.10
0.25
0.2
0.18
0.16
0.05
0.08 0.09
0.01
Intervalos
La curva de densidad que obtenemos en este gráfico tiene una forma de campana que nos
recuerda la campana de Gauss. Para explorar este parecido, vamos a añadir al histograma la
gráfica de la función densidad de una distribución normal de media y desviación típica la media
y la desviación típica muestral del conjunto de datos original, respectivamente; esta función se
define mediante
dnorm(x, mu=media, sd=desviación típica).
11-35
Así,
> hist _ rel ( crw , L . cr )
> curve ( dnorm (x , mean ( crw ) , sd ( crw ) ) , col = " purple " , lty =3 , lwd =2 ,
add = TRUE )
> legend ( " topright " , lwd = c (2 ,2) , lty = c (1 ,2) , col = c ( " red " ," purple " ) ,
legend = c ( " densidad estimada " ," densidad normal " ) )
produce la Figura 11.17. Se observan una ligera diferencia entre la densidad estimada y la
campana de Gauss en la zona central.
densidad estimada
densidad normal
0.20
0.15
Densidades
0.10
0.25
0.2
0.18
0.16
0.05
0.08 0.09
0.01
Intervalos
11-36
x e y son los vectores de primeras y segundas coordenadas de los puntos. Si son las dos
columnas de un data frame de dos variables, lo podemos entrar en su lugar.
nbins sirve para indicar los números de clases: podemos igualarlo a un único valor, y
tomará ese número de clases sobre cada vector, o a un vector de dos entradas que indiquen
el número de clases de cada vector.
col sirve para especificar los colores a usar. Por defecto, los rectángulos vacíos aparecen
de color negro, y el resto se colorean con tonalidades de rojo, de manera que los tonos
más cálidos indican frecuencias mayores.
Además, podemos usar los parámetros usuales de plot para poner un título, etiquetar los ejes,
etc.
A modo de ejemplo, vamos a dibujar el histograma bidimensional de las longitudes y anchuras
de los pétalos de las flores iris, agrupando ambas dimensiones en los números de clases que da
la regla de Freedman-Diaconis (y que calcula la función nclass.FD):
> # Instalamos y cargamos el paquete gplots
...
> hist2d ( iris $ Petal . Length , iris $ Petal . Width , nbins = c ( nclass . FD (
iris $ Petal . Length ) , nclass . FD ( iris $ Petal . Width ) ) )
Al entrar esta última instrucción, obtenemos (junto con una serie de información en la consola
que no hemos copiado) la Figura 11.18, que podéis comparar con el diagrama de dispersión de
los mismos datos de la Figura 10.1.
2.5
2.0
1.5
1.0
0.5
1 2 3 4 5 6
11-37
brewer.pal(n, "paleta predefinida"), que carga en un vector de colores (una paleta) una
secuencia de n colores de la paleta predefinida en el paquete. Los nombres y contenidos
de todas las paletas predefinidas que se pueden usar en esta función se obtienen, en la
ventana de gráficos, ejecutando la instrucción display.brewer.all(). Por ejemplo, la
paleta de colores que se define con
> brewer . pal (11 , " Spectral " )
Vamos a usar esta última paleta en un histograma bidimensional de las alturas de padres e
hijos recogidas por Karl Pearson que ya usamos en el Ejemplo 2.1:
> df _ pearson = read . table ( " http : / / aprender . uib . es / Rdir / pearson . txt " ,
header = TRUE )
> hist2d ( df _ pearson , nbins =30 ,
col = rev ( colorRampPalette ( brewer . pal (11 , " Spectral " ) ) (50) ) )
11-38
75
70
65
60
60 65 70 75
Entonces,
> hist . doble ( df _ pearson ,25)
Hemos «simulado» los histogramas mediante diagramas de barras de sus frecuencias ab-
solutas, para poder dibujar horizontal el de la segunda variable.
El parámetro axes=FALSE en los barplot indica que no dibuje sus ejes de coordenadas.
11-39
75
70
65
60
60 65 70 75
La función par establece los parámetros generales básicos de los gráficos. Como con esta
función los modificamos, guardamos los parámetros anteriores en par.anterior y al final
los restauramos.
El parámetro mar de la función par sirve para especificar, por este orden, los márgenes
inferior, izquierdo, superior y derecho de la próxima figura, en números de líneas.
11-40
nclass.FD calcula el número de clases de un agrupamiento según la regla de Freedman-
Diaconis.
cut sirve para agrupar un vector numérico y codificar sus valores con las clases a las que
pertenecen. Algunos parámetros importantes:
• breaks: sirve para especificar los puntos de corte, el número de clases, o el método
para calcularlo; en estos dos últimos casos, no siempre se obtiene el número de clases
especificado.
• freq: igualado a TRUE, produce el histograma de frecuencias absolutas si los intervalos
son todos de la misma amplitud, y el de frecuencias relativas en caso contrario;
igualado a FALSE, produce siempre el de frecuencias relativas.
• plot: igualado a FALSE, impide que se dibuje el histograma.
• right y include.lowest tienen el mismo significado que en cut.
Internamente, el resultado de hist es una list que incluye los siguientes vectores:
axis añade a un gráfico un eje, con marcas en los lugares indicados por el vector entrado
en el parámetro at.
density calcula una secuencia de puntos sobre la curva de densidad estimada a partir de
un vector numérico.
dnorm define la curva de densidad de una distribución normal. Tiene los dos parámetros
siguientes:
• mu: la media.
• sd: la desviación típica.
11-41
hist2d, del paquete gplots, dibuja histogramas bidimensionales. Dispone de los pará-
metros específicos siguientes:
par sirve para establecer los parámetros generales básicos de los gráficos.
layout divide en sectores la figura a producir, para que pueda incluir varios gráficos
independientes simultáneamente.
11.7. Ejercicio
La tabla lobsters.txt, que hemos guardado en
http://aprender.uib.es/Rdir/lobsters.txt
está formada por los pesos de langostas capturadas en dos zonas; estos pesos están expresados
en kg, con una precisión de 0.01 kg. Las separaciones entre columnas son espacios en blanco y
tiene una primera fila con los nombres de las columnas.
Definid un data frame con esta tabla. Echadle un vistazo (y comprobad que se ha importado
correctamente). Definid dos vectores con los pesos de las langostas de la zona 1 y de la zona 2,
respectivamente.
(b) Para cada zona, y con este agrupamiento, construid un data frame que contenga la tabla
de frecuencias agrupadas.
(c) Para cada zona, y con este mismo agrupamiento, dibujad el correspondiente histograma de
frecuencias relativas incluyendo la curva de densidad estimada. Comparad los dos histogra-
mas: ¿se observa alguna diferencia?
11-42
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo 0
Lecc ón
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Lección 12
Gráficos avanzados
La capacidad que tiene R para representar gráficamente datos es asombrosa y explicar todos
los gráficos que puede producir nos llevaría un curso entero. Por este motivo, en esta lección
nos limitaremos a daros una idea de sus capacidades gráficas, explicando unos cuantos gráficos
avanzados que se pueden producir con R de manera sencilla, sin entrar en el detalle de los
algoritmos que los generan.
El primer parámetro es el lugar de donde R tiene que cargar los documentos a analizar. Si
forman el contenido de un directorio, se especifica con DirSource("directorio"), mientras
que, para cargar el código fuente de una página web, se tiene que usar URISource("url ").
Para conocer el resto de funciones que se pueden usar para entrar este lugar, podéis
consultar la Ayuda de la función Source. En todas ellas se puede especificar con encoding
la codificación de alfabeto; es conveniente hacerlo si los documentos contienen palabras
acentuadas o caracteres especiales.
El parámetro readerControl es una list de dos componentes: reader y language. La
componente reader indica la función que ha de usar R para leer y procesar los documentos
contenidos en el lugar especificado, que dependerá de su formato; aquí solo considerare-
mos el análisis de documentos en formato texto, que corresponde al valor por defecto
de reader, y por lo tanto podemos omitir esta componente en la list (si tenéis curio-
sidad, corresponde a reader=readPlain). Si queréis procesar otro tipo de documentos,
os recomendamos que, al menos al principio, primero los convirtáis en ficheros de texto
ordinario.
Por su lado, el parámetro language indica el idioma en que está el texto, o los textos, a
procesar. Se ha de entrar igualado al código ISO 639-2 del idioma1 entrecomillado: "en"
para inglés, "spa" para español, "tlh" para klingon, etc.
Hemos añadido encoding="UTF-8" porque así es como hemos guardado los ficheros de texto en
nuestro ordenador y como contienen letras acentuadas, mejor curarnos en salud. Como están
en español, hemos especificado language="spa" en readerControl, y en cambio no hemos
especificado la componente reader de esta list porque se trata de ficheros de texto.
A continuación, vamos a inspeccionar el Corpus cargado. Aplicándole la función inspect,
obtenemos cuántos documentos contiene y, para cada uno, su número de caracteres y su formato.
> inspect ( Prueba )
< < VCorpus > >
Metadata : corpus specific : 0 , document level ( indexed ) : 0
Content : documents : 2
[[1]]
< < PlainTextDocument > >
Metadata : 7
Content : chars : 426
[[2]]
< < PlainTextDocument > >
Metadata : 7
Content : chars : 214361
De esta manera vemos que R ha incorporado al VCorpus los documentos por orden alfabético
de su título: el primero es el corto, introR.txt, y el segundo, el largo, lomce.txt.
Con la instrucción writeLines(as.character(VCorpus[[k ]])) obtenemos en la consola el
contenido completo del k-ésimo texto de nuestro VCorpus. Veamos, por ejemplo, el contenido
1
Que podréis encontrar, por ejemplo, en la correspondiente entrada de la Wikipedia: https://en.wikipedia.
org/wiki/List_of_ISO_639-2_codes.
2
Copiado de http://www.boe.es/diario_boe/txt.php?id=BOE-A-2013-12886.
12-2
del texto corto:
> writeLines ( as . character ( Prueba [[1]]) )
La capacidad que tiene R para representar gr á ficamente datos es
asombrosa . Explicar todos los gr á ficos que puede producir R nos
llevar í a un curso entero , por lo que , en este cap í tulo , nos
limitaremos a explicar la versi ó n b á sica de unos cuantos gr á
ficos avanzados que podemos producir con R de manera sencilla ,
para daros una idea de sus capacidades gr á ficas y sin entrar
en detalles de los algoritmos que los producen .
Una vez que hemos guardado una serie de documentos en un VCorpus, tenemos que «lim-
piarlos», en el sentido de eliminar, por ejemplo, signos de puntuación y palabras comunes sin
ningún interés para que no aparezcan en la nube de palabras. Esto se lleva a cabo aplicando la
función tm_map al VCorpus y a una «transformación». Veamos algunos ejemplos:
Convertimos todas las letras en minúsculas, para que R no considere diferentes dos pala-
bras simplemente por la grafía de sus letras:
> Prueba = tm _ map ( Prueba , tolower )
12-3
Otras transformaciones posibles que nosotros no hemos aplicado, son: removeNumbers, para
eliminar los números, y, cargando el paquete SnowballC, la transformación stemDocument (con,
en nuestro ejemplo, language="spanish"), que se queda sólo con las raíces de algunas palabras
(y la nube de palabras que se produciría sería la de estas raíces: así, por ejemplo, en la de la
LOMCE saldría «alumn» en lugar de «alumnos» y «alumnas»).
Ahora estamos en condiciones de construir la nube de palabras de la LOMCE. Para ello,
usaremos la función wordcloud del paquete homónimo. Veamos su aplicación a nuestro caso
concreto, y luego comentamos algunos parámetros:
> # Instalamos y cargamos el paquete " wordcloud "
...
> wordcloud ( Prueba [[2]] , scale = c (3 ,0.5) , max . words =100 ,
rot . per =0.25 , colors = brewer . pal (8 , " Dark2 " ) )
Obtenemos el gráfico 12.1, donde observamos que las palabras más repetidas y, por tanto, en
este sentido más importantes, son «educación», «alumnos», «alumnas», «centros», etc.
necesidades docentes
educativas administraciones
competencias
obligatoria cada oferta
administracióneducativo ciclos aprendizaje
centro correspondiente formación
materia
redacción
sistema
medidas
enseñanzas
opción condicionesacceso
función
orgánica deberán
personal específicas
padres
literatura superior relación
autónomas
admisión
objetivos
evaluación
consejo
caso
profesional
madresforma
alumnos título
bis
adicionalresultados
alumnasapartado
troncales
Figura 12.1. Nube de palabras de la LOMCE.
Como vemos, la función wordcloud se aplica al documento, en este caso Prueba[[2]], y sus
parámetros principales son los siguientes:
scale: un vector de longitud 2 que sirve para indicar la escala, o los tamaños relativos,
de las palabras. Su primera entrada indica el tamaño de la palabra más repetida y la
segunda, el de la palabra menos repetida.
min.freq: la frecuencia mínima necesaria para que una palabra aparezca en el gráfico.
12-4
rot.per: la proporción de palabras que se pueden mostrar giradas 90 grados.
colors: la paleta de colores usados para representar las frecuencias, en orden creciente.
Nosotros hemos usado una paleta del paquete RColorBrewer explicado en la Sección 11.5
(y que se carga automáticamente junto con el paquete wordcloud).
La posición de las palabras en la nube es aleatoria, de manera que puede variar en cada
ejecución de la instrucción.
R procura evitar solapamientos de palabras, y eso hace que, según cómo las vaya colocando,
algunas no le quepan: en este caso os avisará con un warning para cada palabra omitida.
Algunas posibles soluciones son volver a repetir el gráfico hasta que el azar produzca
una combinación en la que quepan todas, reducir el número de palabras en la nube
o reducir el rango de tamaños de las palabras. Otra posibilidad es usar el parámetro
random.order=FALSE, que añade las palabras en orden decreciente de frecuencia, con lo
que si hay palabras que no incluya, serán las menos importantes.
Si, al guardar la imagen, modificáis sus dimensiones, las palabras se recolocarán y puede
que se superpongan (si reducís el gráfico) o se separen (si lo ampliáis). Procurad, al
guardar la imagen, usar la opción «View plot after saving» para controlar el resultado
final.
12-5
hp: Su número de caballos de potencia.
drat: Su relación del eje trasero.
wt: Su peso (en libras/1000).
qsec: El tiempo que tarda en recorrer un cuarto de milla.
vs: El tipo de motor: Motor en V (valor 0) o en línea (valor 1).
am: El tipo de transmisión: 0 automática, 1 manual.
gear: El número de marchas hacia adelante.
carb: El número de carburadores.
Para estudiar los pares de variables que tienen más relación lineal, vamos a representar por
medio de un heatmap la matriz de los valores absolutos de las correlaciones de las variables
no binarias (es decir, de todas menos vs y am, la octava y la novena respectivamente). Las
instrucciones que usamos son las siguientes:
> mtcars2 = mtcars [ , - c (8 ,9) ] # Eliminamos las variables binarias
> round ( abs ( cor ( mtcars2 ) ) ,3)
mpg cyl disp hp drat wt qsec gear carb
mpg 1.000 0.852 0.848 0.776 0.681 0.868 0.419 0.480 0.551
cyl 0.852 1.000 0.902 0.832 0.700 0.782 0.591 0.493 0.527
disp 0.848 0.902 1.000 0.791 0.710 0.888 0.434 0.556 0.395
hp 0.776 0.832 0.791 1.000 0.449 0.659 0.708 0.126 0.750
drat 0.681 0.700 0.710 0.449 1.000 0.712 0.091 0.700 0.091
wt 0.868 0.782 0.888 0.659 0.712 1.000 0.175 0.583 0.428
qsec 0.419 0.591 0.434 0.708 0.091 0.175 1.000 0.213 0.656
gear 0.480 0.493 0.556 0.126 0.700 0.583 0.213 1.000 0.274
carb 0.551 0.527 0.395 0.750 0.091 0.428 0.656 0.274 1.000
> heatmap ( abs ( cor ( mtcars2 ) ) , Rowv = NA , Colv = NA , revC = TRUE )
El gráfico obtenido es la Figura 12.2, y podemos observar la correspondencia entre los colores
de los cuadrados y las entradas de la matriz que representa: cuanto más rojo es el cuadrado
correspondiente a un par de variables, menor es la entrada de la matriz y por lo tanto menos
relación lineal existe entre ellas; y cuanto más claro, mayor es la entrada de la matriz y por
consiguiente su relación lineal. Así, podemos advertir en el gráfico que la variable cyl, el número
de cilindros del vehículo, tiene mucha relación lineal con la cilindrada (disp), el consumo (mpg),
la potencia (hp), el peso (wt) y la relación del eje trasero (drat); en cambio, la variable qsec
(tiempo que tarda el vehículo en recorrer 1/4 de milla) sólo tiene relación lineal con la potencia,
hp, y el número de carburadores, carb.
Antes de continuar, vamos a explicar los parámetros usados en la aplicación anterior de la
función heatmap: con Rowv=NA y Colv=NA impedimos que se añadan al gráfico dendrogramas
por filas y por columnas, respectivamente (véase la Sección 12.3), y con revC=TRUE hemos
indicado que el orden de las variables de izquierda a derecha sea el mismo que de arriba abajo
(por defecto, el orden de las filas se invierte).
La función corrplot del paquete homónimo permite producir mapas de colores de matrices
de correlaciones más completos:
12-6
mpg
cyl
disp
hp
drat
wt
qsec
gear
carb
mpg
cyl
disp
hp
drat
wt
qsec
gear
carb
Figura 12.2. Heatmap de la matriz de correlaciones de la tabla
de datos mtcars.
El resultado aparece en el gráfico de la izquierda de la Figura 12.3. En este caso hemos usado
la matriz de correlaciones sin valor absoluto, porque no hace falta: los círculos más grandes
indican una mayor correlación en valor absoluto y, por tanto, más relación lineal entre las
variables involucradas, y los colores indican el signo de la correlación según el código descrito
en la columna de la derecha. Si además hubiéramos querido que apareciera el valor de la
correlación para cada par de variables, hubiéramos podido utilizar las opciones siguientes:
> corrplot ( cor ( mtcars2 ) , method = " shade " , shade . col = NA ,
tl . col = " black " , tl . srt =45 , addCoef . col = " black " )
Ejemplo 12.2. Ahora vamos a visualizar por medio de un heatmap las diferencias entre los
vehículos estudiados en la tabla mtcars. Las diferencias las cuantificaremos por medio de la
distancia euclídea entre los vectores que definen las filas de la tabla de datos. Como en el
ejemplo anterior, sólo vamos a considerar las variables numéricas, así que usaremos el data
frame mtcars2. A modo de ejemplo, las filas de los vehículos Mazda RX4 y Valiant son
> mtcars2 [ c ( " Mazda RX4 " ," Valiant " ) ,]
mpg cyl disp hp drat wt qsec gear carb
Mazda RX4 21.0 6 160 110 3.90 2.62 16.46 4 4
Valiant 18.1 6 225 105 2.76 3.46 20.22 3 1
12-7
qsec
gear
mpg
carb
disp
drat
ar
pg
b
sp
e
at
cyl
hp
r
wt
ge
qs
hp
cy
ca
m
t
dr
di
w
1 1
mpg mpg 1 −0.85 −0.85 −0.78 0.68 −0.87 0.42 0.48 −0.55
0.8 0.8
cyl cyl −0.85 1 0.9 0.83 −0.7 0.78 −0.59 −0.49 0.53
0.6 0.6
disp disp −0.85 0.9 1 0.79 −0.71 0.89 −0.43 −0.56 0.39
0.4 0.4
drat 0 drat 0.68 −0.7 −0.71 −0.45 1 −0.71 0.09 0.7 −0.09 0
−0.2 wt −0.87 0.78 0.89 0.66 −0.71 1 −0.17 −0.58 0.43 −0.2
wt
−0.4 −0.4
qsec qsec 0.42 −0.59 −0.43 −0.71 0.09 −0.17 1 −0.21 −0.66
−0.6 −0.6
gear gear 0.48 −0.49 −0.56 −0.13 0.7 −0.58 −0.21 1 0.27
−0.8 −0.8
carb carb −0.55 0.53 0.39 0.75 −0.09 0.43 −0.66 0.27 1
−1 −1
y su distancia euclídea es
p
(21 18.1)2 + (6 6)2 + (160 225)2 + (110 105)2 + · · · + (4 1)2 = 65.4565.
Para hallar la matriz de distancias euclídeas entre las filas de la tabla de datos mtcars2
usamos la instrucción dist:
> dist . mtcars = as . matrix ( dist ( mtcars2 ) )
> round ( dist . mtcars [1:4 ,1:4] ,2) # Submatriz de las 4 primeras filas
y columnas
Mazda RX4 Mazda RX4 Wag Datsun 710 Hornet 4 Drive
Mazda RX4 0.00 0.62 54.90 98.10
Mazda RX4 Wag 0.62 0.00 54.88 98.09
Datsun 710 54.90 54.88 0.00 150.99
Hornet 4 Drive 98.10 98.09 150.99 0.00
La función dist calcula por defecto las distancias euclídeas entre las filas de una matriz o
un data frame de variables numéricas. Para calcular alguna otra distancia, se ha de especificar
con el parámetro method: consultad la Ayuda de la función para conocer los posibles valores de
este parámetro. En el código anterior, hemos usado la función as.matrix para transformar el
resultado de dist en una matriz ya que esta última función produce un objeto de clase dist y
queremos que el resultado sea una matriz para poderle aplicar la función heatmap.
Dibujemos el heatmap de las distancias entre vehículos; el resultado es la Figura 12.4.
> heatmap ( dist . mtcars , margins = c (9 ,9) , symm = TRUE , Rowv = NA , Colv = NA ,
revC = TRUE )
El parámetro margins especifica el ancho de los márgenes donde se escriben los nombres de
las filas y las columnas y lo hemos adaptado para que quepan los nombres de los vehículos; el
parámetro symm=TRUE indica a R que ha de considerar que la matriz es simétrica. El resto de
los parámetros ya los hemos explicado antes. Esta función dispone de muchos más parámetros,
que podéis consultar en su Ayuda.
12-8
La representación visual tiene el mismo significado que antes, pero referida a la matriz a la
que hemos aplicado la función heatmap: cuanto más roja es la casilla, menor es la distancia entre
los vehículos, y por lo tanto más parecidos son. Así, por ejemplo, si observamos la columna del
vehículo «Mazda RX4», vemos que los vehículos más diferentes a él son «Cadillac Fleetwood»,
«Lincoln Continental» y «Chrysler Imperial», ya que aparecen con un color más cercano al
blanco, y los más parecidos son «Mazda RX4 Wag», «Merc 280», «Merc 280C», «Merc 230»,
«Volvo 142E», «Toyota Corona», «Porsche 914-2», «Merc 240D», etc.
Mazda RX4
Mazda RX4 Wag
Datsun 710
Hornet 4 Drive
Hornet Sportabout
Valiant
Duster 360
Merc 240D
Merc 230
Merc 280
Merc 280C
Merc 450SE
Merc 450SL
Merc 450SLC
Cadillac Fleetwood
Lincoln Continental
Chrysler Imperial
Fiat 128
Honda Civic
Toyota Corolla
Toyota Corona
Dodge Challenger
AMC Javelin
Camaro Z28
Pontiac Firebird
Fiat X1−9
Porsche 914−2
Lotus Europa
Ford Pantera L
Ferrari Dino
Maserati Bora
Volvo 142E
Mazda RX4
Mazda RX4 Wag
Datsun 710
Hornet 4 Drive
Hornet Sportabout
Valiant
Duster 360
Merc 240D
Merc 230
Merc 280
Merc 280C
Merc 450SE
Merc 450SL
Merc 450SLC
Cadillac Fleetwood
Lincoln Continental
Chrysler Imperial
Fiat 128
Honda Civic
Toyota Corolla
Toyota Corona
Dodge Challenger
AMC Javelin
Camaro Z28
Pontiac Firebird
Fiat X1−9
Porsche 914−2
Lotus Europa
Ford Pantera L
Ferrari Dino
Maserati Bora
Volvo 142E
12.3. Dendrogramas
Dada una tabla de datos podemos construir una matriz de distancias entre los individuos
representados en ella, aplicando una distancia concreta a cada par de filas; es lo que hicimos en
el Ejemplo 12.2, donde calculamos la distancia euclídea entre cada par de descripciones de tipo
de coche. Una vez construida dicha matriz de distancias, podemos ir agrupando los individuos
de la tabla de datos usando algún algoritmo de agrupamiento, o «clustering», jerárquico.
Muchos algoritmos de clustering jerárquico siguen una misma estrategia. En un primer paso,
se agrupan los dos individuos más parecidos (más cercanos según la distancia usada) en un
grupo y se los sustituye por este grupo, al que se considera un nuevo individuo «virtual». A
partir de aquí, en cada paso, se agrupan en un nuevo grupo los dos individuos (originales o
«virtuales») más cercanos y se los sustituye por el grupo que forman. El algoritmo termina
cuando queda un único grupo. Los diferentes algoritmos de este tipo, denominados genérica-
mente «aglomerativos», se distinguen básicamente según la manera como se define la distancia
entre grupos a partir de las distancias entre sus individuos; aquí no entraremos en detalle y
12-9
nos limitaremos a usar el algoritmo que lleva implementado R por defecto (el llamado méto-
do de enlace completo: la distancia entre dos grupos es el máximo de las distancias entre sus
elementos).3
Un dendrograma es entonces una representación gráfica del orden en el que se han ido realizan-
do dichas agrupaciones y de las distancias entre los pares de individuos (originales y «virtuales»)
que se han ido uniendo. Así podemos visualizar qué individuos se parecen más o si aparece de
manera natural alguna clasificación de los individuos.
Veamos un ejemplo detallado. La tabla de datos all.mammals.milk.1956 que lleva el paquete
cluster.datasets contiene información sobre 5 variables numéricas relativas a la composición
de la leche materna de 25 especies de mamíferos. Las variables de esta tabla de datos son:
Démosle un vistazo.
> # Instalamos y cargamos el paquete " cluster . datasets "
...
> data ( all . mammals . milk .1956)
> AMM = all . mammals . milk .1956
> str ( AMM )
’ data . frame ’: 25 obs . of 6 variables :
$ name : chr " Horse " " Orangutan " " Monkey " " Donkey " ...
$ water : num 90.1 88.5 88.4 90.3 90.4 87.7 86.9 82.1 81.9 81.6
...
$ protein : num 2.6 1.4 2.2 1.7 0.6 3.5 4.8 5.9 7.4 10.1 ...
$ fat : num 1 3.5 2.7 1.4 4.5 3.4 1.7 7.9 7.2 6.3 ...
$ lactose : num 6.9 6 6.4 6.2 4.4 4.8 5.7 4.7 2.7 4.4 ...
$ ash : num 0.35 0.24 0.18 0.4 0.1 0.71 0.9 0.78 0.85 0.75 ...
> head ( AMM )
name water protein fat lactose ash
1 Horse 90.1 2.6 1.0 6.9 0.35
2 Orangutan 88.5 1.4 3.5 6.0 0.24
3 Monkey 88.4 2.2 2.7 6.4 0.18
4 Donkey 90.3 1.7 1.4 6.2 0.40
5 Hippo 90.4 0.6 4.5 4.4 0.10
6 Camel 87.7 3.5 3.4 4.8 0.71
3
Para más información sobre algoritmos de clustering, podéis consultar http://en.wikipedia.org/wiki/
Cluster_analysis.
12-10
Para cuantificar la diferencia entre las composiciones de la leche de estos mamíferos, usaremos
de nuevo la distancia euclídea entre sus vectores de valores de variables numéricas.
> dist . AMM = dist ( AMM [ ,2:6])
> round ( as . matrix ( dist . AMM ) [1:6 ,1:6] ,3)
1 2 3 4 5 6
1 0.000 3.327 2.494 1.226 4.759 4.107
2 3.327 0.000 1.206 2.794 2.798 2.592
3 2.494 1.206 0.000 2.375 3.716 2.348
4 1.226 2.794 2.375 0.000 3.763 4.007
5 4.759 2.798 3.716 3.763 0.000 4.176
6 4.107 2.592 2.348 4.007 4.176 0.000
Hemos transformado el objeto dist en una matriz para poder consultar algunas entradas, pero
lo mantenemos como objeto de tipo dist para construir su dendrograma.
Una vez se dispone de la matriz de distancias entre los individuos (en este caso, mamíferos),
se calcula su clustering jerárquico aplicándole la función hclust y se convierte el resultado de
esta función en un dendrograma usando as.dendrogram. La función hclust permite especificar
el tipo de algoritmo de agrupamiento que queremos usar por medio del parámetro method; aquí
usaremos el método por defecto.
> dend . AMM = as . dendrogram ( hclust ( dist . AMM ) )
Y ahora ya podemos dibujar este dendrograma. Hay muchas opciones para hacerlo con R. La
básica es aplicarle simplemente la función plot:
> plot ( dend . AMM )
Obtenemos el gráfico de la izquierda de la Figura 12.5. En este gráfico, los grupos se representan
mediante líneas horizontales y las alturas representan distancias, de manera que la altura a la
que se unen dos grupos es la distancia entre ellos. Así, este dendrograma nos muestra, por
ejemplo, que la composición de la leche de los animales 24 y 25 (foca y delfín) es diferente de
la del resto, formando un grupo propio que no se ha unido al resto de animales hasta el último
paso. En cambio, la ballena (el 23), que comparte hábitat y dieta con ellos, se agrupa con el
ciervo y el reno (21 y 22). Por otro lado, cortando por una línea horizontal imaginaria a altura
30, podemos observar que se forman tres grupos muy claros.
En un dendrograma producido a partir de las distancias entre las filas de un data frame, los
individuos se representan mediante los identificadores de las filas: en nuestro ejemplo, números.
Si queremos los nombres de los animales como etiquetas, una posibilidad es modificar el data
frame AMM usando como identificadores de las filas los nombres de los animales; y ya que estamos,
podemos eliminar esta variable del data frame resultante:
> AMM . nombres = AMM
> rownames ( AMM . nombres ) = AMM . nombres $ name
> AMM . nombres = AMM . nombres [ , - 1]
> head ( AMM . nombres )
water protein fat lactose ash
Horse 90.1 2.6 1.0 6.9 0.35
Orangutan 88.5 1.4 3.5 6.0 0.24
Monkey 88.4 2.2 2.7 6.4 0.18
12-11
Donkey 90.3 1.7 1.4 6.2 0.40
Hippo 90.4 0.6 4.5 4.4 0.10
Camel 87.7 3.5 3.4 4.8 0.71
> plot ( as . dendrogram ( hclust ( dist ( AMM . nombres ) ) ) )
El resultado es el gráfico de la derecha de la Figura 12.5, donde ahora podemos leer (más o
menos) los nombres de los animales sin tener que ir a consultar el data frame.
Aparte de los parámetros usuales de la función plot, dos parámetros interesantes cuando se
aplica a un dendrograma son horiz=TRUE, que lo dibuja horizontal, y type="triangle", que
dibuja las ramificaciones triangulares en vez de rectangulares.
60
60
50
50
40
40
30
30
20
20
10
10
0
0
24
25
10
8
11
16
9
14
7
15
6
12
5
2
3
1
4
13
17
19
20
18
23
21
22
Seal
Dolphin
Cat
Buffalo
Fox
Sheep
Guinea Pig
Pig
Bison
Zebra
Camel
Llama
Hippo
Orangutan
Monkey
Horse
Donkey
Mule
Dog
Rabbit
Rat
Elephant
Whale
Deer
Reindeer
Figura 12.5. Dendrogramas de los mamíferos clasificados se-
gún la composición de su leche.
Para tener más control sobre el aspecto de un dendrograma, por ejemplo cambiar sus etique-
tas sin modificar el data frame original, escribirlas en un tamaño menor o mayor o colorear
ramas o etiquetas a fin de añadir información al dendrograma, lo más sencillo es utilizar al-
gunas funciones específicas aportadas por diversos paquetes. Aquí vamos a explicar algunas
funcionalidades del paquete dendextend.
A modo de ejemplo, una vez cargado este paquete, para cambiar directamente las etiquetas
del dendrograma del data frame AMM, podemos usar de manera natural la función labels, que
nos da las etiquetas de las hojas.
> Instalamos y cargamos el paquete " dendextend "
...
> L = labels ( dend . AMM )
> L
[1] 24 25 10 8 11 16 9 14 7 15 6 12 5 2 3 1 4 13 17 19 20
[22] 18 23 21 22
> labels ( dend . AMM ) = AMM $ name [ L ]
> labels ( dend . AMM )
[1] " Seal " " Dolphin " " Cat " " Buffalo " " Fox "
[6] " Sheep " " Guinea Pig " " Pig " " Bison " " Zebra "
[11] " Camel " " Llama " " Hippo " " Orangutan " " Monkey "
[16] " Horse " " Donkey " " Mule " " Dog " " Rabbit "
[21] " Rat " " Elephant " " Whale " " Deer " " Reindeer "
12-12
La función labels aplicada a un dendrograma nos da las etiquetas de las hojas de izquier-
da a derecha, y con el paquete dendextend nos permite también modificar estas etiquetas.
Naturalmente, a cada etiqueta (en nuestro ejemplo, número de fila) le tenemos que hacer co-
rresponder el nombre del animal que le toca: por eso usamos labels(dend.AMM)=AMM$name[L]
y no labels(dend.AMM)=AMM$name a secas, que a la primera etiqueta del dendrograma (la 25)
le asignaría el primer nombre de animal, correspondiente a la etiqueta 1.
Ahora que ya hemos modificado las etiquetas del dendrograma, podemos modificar su aparien-
cia de cara a representarlo gráficamente. Para ello podemos usar la función set, cuya sintaxis
básica es
set(dendrograma, what="característica", valor )
Encontraréis la lista completa de las características que podemos usar en la Ayuda de la fun-
ción. Pero cuidado, set no modifica el dendrograma permanentemente, solo temporalmente. Si
queréis modificar el dendrograma, tenéis que usar
dendrograma=set(dendrograma, what="característica", valor )
Si queremos cambiar más de una característica de golpe, es muy cómodo usar la notación
«encadenada» de funciones, en la que a un objeto se le van aplicando funciones una tras otra
separándolas con el signo %>%.4 En este caso, no se entra el dendrograma dentro de set, sino
al principio de la cadena. Por ejemplo, para reducir el tamaño de letra de las etiquetas (para
que así quepan en el gráfico) y escribirlas en rojo, podemos entrar el código siguiente:
> dend . AMM %> %
set ( what = " labels _ col " , " red " ) %> % # Colores de las etiquetas
set ( what = " labels _ cex " , 0.8) %> % # Tama ñ o de las etiquetas
plot ( main = " Dendrograma con etiquetas coloreadas \ n y reducidas " )
Para distinguir visualmente los principales grupos que se forman en el dendrograma, y por
ejemplo usar un color en cada grupo, podemos usar el parámetro k igualado al número
de grupos que queremos reconocer. Por ejemplo,
> dend . AMM %> %
set ( what = " labels _ cex " , 0.8) %> %
set ( what = " labels _ col " , c ( " red " ," blue " ," green " ) , k =3) %> %
set ( what = " branches _ k _ color " ,c ( " red " ," blue " ," green " ) ,k =3) %> %
# Colores de las ramas
plot ( main = " Dendrograma con 3 grupos resaltados " )
12-13
Dendrograma con etiquetas coloreadas
y reducidas
60
50
40
30
20
10
0
Seal
Dolphin
Cat
Buffalo
Fox
Sheep
Guinea Pig
Pig
Bison
Zebra
Camel
Llama
Hippo
Orangutan
Monkey
Horse
Donkey
Mule
Dog
Rabbit
Rat
Elephant
Whale
Deer
Reindeer
Figura 12.6. Dendrograma usando la función set de
dendextend.
Ahora colorearemos las etiquetas del dendrograma según su dieta, y aprovecharemos para
ver otra manera de usar set, definiendo un nuevo dendrograma dend.AMM2 donde los
cambios realizados sobre dend.AMM por las diferentes aplicaciones de set sean permanen-
tes.
> dend . AMM2 = dend . AMM %> %
set ( what = " labels _ cex " , 0.8) %> % # Tama ñ o de etiquetas
set ( what = " labels " , AMM $ name [ L ]) %> % # Nombres en las
etiquetas
set ( what = " labels _ col " , as . numeric ( AMM $ diet [ L ]) ) # Colores
seg ú n la dieta
12-14
Dendrograma con 3 grupos resaltados
60
50
40
30
20
10
0
Seal
Dolphin
Cat
Buffalo
Fox
Sheep
Guinea Pig
Pig
Bison
Zebra
Camel
Llama
Hippo
Orangutan
Monkey
Horse
Donkey
Mule
Dog
Rabbit
Rat
Elephant
Whale
Deer
Reindeer
Figura 12.7. Dendrograma con etiquetas y aristas coloreadas
en 3 grupos.
> plot ( dend . AMM2 , main = " Dendrograma de mam í feros clasificados
\ n seg ú n la dieta " )
Seal
Dolphin
Cat
Buffalo
Fox
Sheep
Guinea Pig
Pig
Bison
Zebra
Camel
Llama
Hippo
Orangutan
Monkey
Horse
Donkey
Mule
Dog
Rabbit
Rat
Elephant
Whale
Deer
Reindeer
Para más información sobre las posibilidades de dendextend y su interacción con otros pa-
quetes que dibujan dendrogramas, consultad https://cran.r-project.org/web/packages/
dendextend/vignettes/introduction.html. Por ejemplo, podemos modificar un dendrogra-
ma con set y luego aplicarle la función circlize_dendrogram del paquete circlize para
obtener una representación circular. Así, el código siguiente produce la Figura 12.9.
12-15
> # Instalamos y cargamos el paquete " circlize "
...
> circlize _ dendrogram ( dend . AMM2 )
Rabbit
Dog
Mule
t
Do
Ra
nt
nk
ha
ey
ep
El
Ho
rse
le
ha
W
Mo
nke
y
er
De
Oran
gutan
r
Reindee
Hippo
Seal
a
Llam
Dolp
hin
l
me
Ca
Ca
t
ra
Z eb
Bu
f
on
fa
lo
Bis
Fox
Pig
Sheep
Guinea Pig
Y finalmente, se pueden usar los parámetros usuales de plot; para más información, incluida
la lista de figuras disponibles, consultad la Ayuda de la función.
Veamos un ejemplo. Consideremos la tabla de datos savings que lleva el paquete faraway.
Dicha tabla de datos nos da los 5 indicadores económicos siguientes de 50 países durante el
período 1960–1970:
12-16
pop15: su porcentaje de población menor de 15 años.
12-17
> text ( savings $ dpi , savings $ sr , rownames ( savings ) , cex =0.75)
Japan Japan
20
20
Zambia Zambia
Denmark Denmark
Malta Malta
15
15
Netherlands Netherlands
Italy Switzerland Italy Switzerland
South Rhodesia Belgium South Rhodesia Belgium
Tasa de ahorro
Tasa de ahorro
Philippines
Peru Brazil France Philippines
Peru Brazil France
Portugal Germany Portugal Germany
China Spain Austria China Spain Austria
Australia Australia
South Africa Ireland Finland South Africa Ireland Finland
Costa Rica Greece New Zealand Costa Rica Greece New Zealand
Luxembourg
Norway Luxembourg
Norway
10
10
Uruguay
Venezuela Uruguay
Venezuela
India
Libya Canada India
Libya Canada
Jamaica
Honduras United Kingdom Jamaica
Honduras United Kingdom
Nicaragua United States Nicaragua United States
Sweden Sweden
Bolivia Bolivia
Turkey Turkey
5
Colombia
5
Malaysia Colombia
Malaysia
Panama Panama
Korea Korea
Ecuador Ecuador
Guatamala
Tunisia Guatamala
Tunisia
Paraguay Paraguay
Iceland Iceland
Chile Chile
0
0
0 1000 2000 3000 4000 0 1000 2000 3000 4000
Renta per cápita Renta per cápita
Ahora que ya lo tenemos cargado, vamos a producir un streamgraph de los diferentes géneros
de películas desde los años 30 hasta la actualidad. Para ello, usaremos la tabla de datos movies,
con información de 58,788 películas, que lleva el paquete ggplot2movies.
> # Instalamos y cargamos el paquete " ggplot2movies "
...
5
Mayo de 2016. A lo mejor cuando leáis estas notas, sí.
12-18
> data ( movies )
> str ( movies )
Classes ’ tbl _ df ’ , ’ tbl ’ and ’ data . frame ’: 58788 obs . of 24
variables :
$ title : chr " $ " " $ 1000 a Touchdown " " $ 21 a Day Once a
Month " " $ 40 ,000 " ...
$ year : int 1971 1939 1941 1996 1975 2000 2002 2002 1987
1917 ...
$ length : int 121 71 7 70 71 91 93 25 97 61 ...
$ budget : int NA NA NA NA NA NA NA NA NA NA ...
$ rating : num 6.4 6 8.2 8.2 3.4 4.3 5.3 6.7 6.6 6 ...
$ votes : int 348 20 5 6 17 45 200 24 18 51 ...
$ r1 : num 4.5 0 0 14.5 24.5 4.5 4.5 4.5 4.5 4.5 ...
$ r2 : num 4.5 14.5 0 0 4.5 4.5 0 4.5 4.5 0 ...
$ r3 : num 4.5 4.5 0 0 0 4.5 4.5 4.5 4.5 4.5 ...
$ r4 : num 4.5 24.5 0 0 14.5 14.5 4.5 4.5 0 4.5 ...
$ r5 : num 14.5 14.5 0 0 14.5 14.5 24.5 4.5 0 4.5 ...
$ r6 : num 24.5 14.5 24.5 0 4.5 14.5 24.5 14.5 0 44.5 ...
$ r7 : num 24.5 14.5 0 0 0 4.5 14.5 14.5 34.5 14.5 ...
$ r8 : num 14.5 4.5 44.5 0 0 4.5 4.5 14.5 14.5 4.5 ...
$ r9 : num 4.5 4.5 24.5 34.5 0 14.5 4.5 4.5 4.5 4.5 ...
$ r10 : num 4.5 14.5 24.5 45.5 24.5 14.5 14.5 14.5 24.5
4.5 ...
$ mpaa : chr " " " " " " " " ...
$ Action : int 0 0 0 0 0 0 1 0 0 0 ...
$ Animation : int 0 0 1 0 0 0 0 0 0 0 ...
$ Comedy : int 1 1 0 1 0 0 0 0 0 0 ...
$ Drama : int 1 0 0 0 0 1 1 0 1 0 ...
$ Documentary : int 0 0 0 0 0 0 0 1 0 0 ...
$ Romance : int 0 0 0 0 0 0 0 0 0 0 ...
$ Short : int 0 0 1 0 0 0 0 1 0 0 ...
De esta tabla de datos nos vamos a quedar solo con las variables year (año de estreno) y
Action, Animation, Comedy, Drama, Documentary, Romance y Short, variables binarias que
indican si la película entra o no en cada una de las categorías siguientes: acción, animación,
comedia, drama, documental, romántica y cortometraje, respectivamente.
> movies . small = movies [ , c (2 ,18:24) ]
> str ( movies . small )
Classes ’ tbl _ df ’ , ’ tbl ’ and ’ data . frame ’: 58788 obs . of 8
variables :
$ year : int 1971 1939 1941 1996 1975 2000 2002 2002 1987
1917 ...
$ Action : int 0 0 0 0 0 0 1 0 0 0 ...
$ Animation : int 0 0 1 0 0 0 0 0 0 0 ...
$ Comedy : int 1 1 0 1 0 0 0 0 0 0 ...
$ Drama : int 1 0 0 0 0 1 1 0 1 0 ...
$ Documentary : int 0 0 0 0 0 0 0 1 0 0 ...
$ Romance : int 0 0 0 0 0 0 0 0 0 0 ...
$ Short : int 0 0 1 0 0 0 0 1 0 0 ...
12-19
Ahora hemos de modificar el data frame para poderlo usar como una serie temporal, donde
para cada año y para cada categoría nos dé el correspondiente número de películas. La manera
más sencilla es, en primer lugar, usar la función gather del paquete tidyr, especializado en
funciones para «limpiar» y reorganizar datos. Con esta función podemos sustituir las variables
binarias correspondientes a las categorías por una nueva variable factor (la llamaremos genero)
cuyos valores sean los nombres de dichas variables y añadir una nueva variable binaria (la
llamaremos valor) que da el valor de la variable binaria original. La sintaxis es
gather(data frame, nuevo factor , nueva variable numérica, variables agrupadas)
De esta manera, en nuestro caso, cada fila de movies.small se convierte en 7 filas del nuevo
data frame, una para cada variable «agrupada» en la nueva variable genero. A continuación,
observamos que menos del 0.1 % de las películas se filmaron antes de 1930, por lo que en nuestro
streamgraph esos años van a aparecer muy delgados, así que los eliminamos.
> Instalamos y cargamos el paquete " tidyr "
...
> movies . agrup = gather ( movies . small , genero , valor , - year ) # - year
indica " todas las variables menos year "
> str ( movies . agrup )
Classes ’ tbl _ df ’ , ’ tbl ’ and ’ data . frame ’: 411516 obs . of 3
variables :
$ year : int 1971 1939 1941 1996 1975 2000 2002 2002 1987 1917
...
$ genero : chr " Action " " Action " " Action " " Action " ...
$ valor : int 0 0 0 0 0 0 1 0 0 0 ...
> quantile ( movies . agrup $ year ,0.001)
0.1 %
1930
> movies . agrup = movies . agrup [ movies . agrup $ year >=1930 ,]
A continuación, para cada año y cada categoría, sumamos los valores de la variable valor:
como son ceros y unos, esto nos dará, los números anuales de películas de cada categoría.
Una posible manera de hacerlo es con el código siguiente (la suma de dos factores indica que
agrupamos los datos por todas la combinaciones de un nivel de cada factor):
> sum . movies . agrup = aggregate ( valor ~ year + genero , data = movies . agrup ,
FUN = sum )
> str ( sum . movies . agrup )
’ data . frame ’: 532 obs . of 3 variables :
$ year : int 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939
...
$ genero : chr " Action " " Action " " Action " " Action " ...
$ valor : int 4 5 5 6 8 17 9 14 8 17 ...
12-20
> streamgraph ( sum . movies . agrup , " genero " , " valor " , " year " ,
interactive = FALSE )
Obtenemos el gráfico de la Figura 12.11. En este gráfico, la variable tiempo (en nuestro caso,
year) se representa en el eje horizontal, y las franjas de colores corresponden, de abajo arriba,
a los niveles ordenados del factor genero: la franja roja inferior a las películas de acción, la
naranja inmediatamente superior a las de animación, y así hasta llegar a la franja azul de los
cortometrajes. Su grueso, sobre cada año, representa el número de películas de cada género.
se añade a la figura un menú desplegable con los diferentes niveles del factor usado (en nuestro
caso genero) que permite ver la evolución de ese nivel. Esta figura se puede guardar como
imagen interactiva o como página web. En la Figura 12.12 se puede ver una captura de pantalla
del resultado.
12-21
Figura 12.12. Captura de pantalla del streamgraph anterior
producido en modo interactivo.
• x: indica el lugar de donde se han de cargar los documentos. Algunos posibles valores:
DirSource("directorio", encoding=...) y URISource("url ", encoding=...).
• readerControl: se ha de igualar a una list de dos componentes: reader, que
indica la función con la que se han de leer los documentos, y language, que indica
el idioma de los documentos. Si los ficheros son de texto, no es necesario especificar
la componente reader.
inspect, del paquete tm, nos da el número de documentos de un VCorpus y, de cada uno
de ellos, su formato y su número de caracteres.
writeLines(as.character(documento)) escribe el documento en la consola.
tm_map(VCorpus, transformación), del paquete tm, aplica la transformación a todos los
documentos del VCorpus. Algunas transformaciones:
• tolower: convierte todas las letras en minúsculas.
• removePunctuation: elimina los signos de puntuación.
• removeWords: elimina las palabras del vector que se especifique.
• stripWhitespace: elimina los espacios en blanco extra.
• removeNumbers: elimina los números.
• stemDocument: cargando el paquete SnowballC, se queda sólo con las raíces de al-
gunas palabras.
stopwords, del paquete tm, aplicada al nombre de un idioma, entrecomillado, produce
una lista de palabras «comodín» de este idioma.
wordcloud, del paquete homónimo, produce una nube de palabras del documento al que
se aplica. Algunos parámetros destacables:
• scale: un vector de longitud 2 que sirve para indicar el tamaño relativo de las
palabras de mayor y menor frecuencia.
12-22
• max.words: el número máximo de palabras a mostrar.
• min.freq: la frecuencia mínima necesaria para que una palabra aparezca en el grá-
fico.
• rot.per: la proporción de palabras que se pueden mostrar giradas 90 grados.
• colors: la paleta de colores usada para representar las frecuencias.
• Rowv y Colv: igualados a NA, impiden que se añadan dendrogramas de las filas y las
columnas en los márgenes.
• revC: igualado a TRUE invierte el orden de las columnas.
• method: especifica la forma de las casillas; algunos valores posibles son "circle"
(círculos, el valor por defecto), "square" (cuadrados), "shade" (la casilla sombrea-
da).
• addshade: cuando usamos method="shade", permite especificar si queremos marcar
las casillas correspondientes a correlación positiva o negativa (o ambas), mediante
rectas de pendiente positiva o negativa.
• shade.col: permite especificar el color de las rectas anteriores; igualado a NA, no las
añade.
• tl.col: el color de las etiquetas de filas y columnas.
• tl.srt: la inclinación de las etiquetas de filas y columnas.
• addCoef.col: igualado a un color, especifica que se escriban en cada casilla la entrada
correspondiente de la matriz en ese color.
dist, aplicada a una matriz o a un data frame de variables numéricas, calcula la matriz
de distancias entre sus filas. El tipo de distancia se especifica con el parámetro method y
por defecto es la euclídea.
hclust calcula un clustering jerárquico de una matriz de distancias calculada por medio
de dist. El parámetro method permite especificar el algoritmo concreto.
12-23
set(dendrograma, what="característica", valor ), del paquete dendextend, permite mo-
dificar la característica del dendrograma. Algunas características útiles:
install_github("url "), del paquete devtools, instala un paquete del que conocemos
su url en GitHub.
gather, del paquete tidyr, sustituye en un data frame un conjunto de variables numéricas
por dos variables: un factor cuyos niveles son los nombres de las variables numéricas
originales y una variable numérica nueva que da, para cada nivel de ese factor, el valor
de la variable original. De esta manera, cada fila del data frame original se desdobla en
tantas filas como variables hemos agrupado en el nuevo factor.
12-24
AprendeR:
Introducción al tratamiento
de datos con R y RStudio
Módulo
Lecc ón
Campus Extens
UIB Virtual http://moocs.uib.cat
Edita: Campus Extens – UIB Virtual
Disseny portada: Direcció de l'Estratègia de Comunicació i Promoció Institucional (dircom.uib.cat)
Extras de R Markdown
Para la mayor parte de las necesidades de este curso en lo que se refiere a composición de ficheros
R markdown, el documento Markdown Quick Reference que lleva RStudio y que mencionamos
en la Lección 0 es suficiente. No obstante, a lo largo del curso ampliaremos sus contenidos en
algunos temas cuando lo creamos necesario. En esta lección, vamos a tratar diversos puntos:
cómo escribir fórmulas matemáticas bien formateadas; cómo controlar el comportamiento de los
bloques de código (chunks) al compilar el fichero R markdown, y su aspecto en el documento
final; cómo controlar el aspecto de los gráficos producidos en los chunks; y como incluir de
manera sencilla tablas bien formateadas.
1. Fórmulas matemáticas
La manera de incluir fórmulas matemáticas en R Markdown se basa en la sintaxis del sistema
de composición de textos científicos LATEX, que es el que hemos usado para escribir las lecciones
de este curso. Esta misma sintaxis, con pequeñas modificaciones, se usa para escribir fórmulas
matemáticas bien formateadas en otros contextos: en los foros de Moodle, en las entradas y
comentarios de blogs en Blogger o Wordpress, en la Wikipedia, etc.
Incluir fórmulas en un texto R Markdown no tiene ningún misterio. Solo hay que introducir
el código que representa la fórmula de una de las dos formas siguientes:
Para las fórmulas o ecuaciones dentro del mismo párrafo, se escribe el código entre dos
dólares: $código$.
Para las fórmulas o ecuaciones que queramos que aparezcan centradas en una línea aparte,
se escribe el código entre dos dobles dólares: $$código$$.
Al componer una fórmula a partir del código, RStudio ignora los espacios en blanco que
hayamos escrito en ella, y añade los espacios en blanco a partir del significado lógico de sus
elementos. Por ejemplo (y dejamos algunos espacios en blanco innecesarios para que veáis que
no tienen ningún efecto en el resultado), el código siguiente:
p
b ± b2 4ac
x= .
2a
Observad el código:
1
La raíz cuadrada de algo se indica con \sqrt{algo} (de square root).
Los signos ± y 6= se indican con las marcas \pm (de plus-minus) y \neq (de not equal),
respectivamente.
Como podéis ver, las marcas de LATEX que definen los diferentes elementos de las fórmulas
matemáticas tienen nombres intuitivamente claros.
A continuación damos algunas tablas con las marcas correspondientes a algunos de los signos
matemáticos más usuales:
Algunos operadores
P Q ` L
\sum \prod \coprod \bigoplus
T S F R
\bigcap \bigcup \bigsqcup \int
W V
\bigvee \bigwedge
Algunos delimitadores
( ( ) ) [ [ ] ]
{ \{ } \} h \langle i \rangle
b \lfloor c \rfloor d \lceil e \rceil
2
Algunos acentos «expansibles»
xg
yz \widetilde{xyz} xd
yz \widehat{xyz}
xyz \overline{xyz} xyz \underline{xyz}
xyz \overleftarrow{xyz} !
xyz \overrightarrow{xyz}
z}|{
xyz \overbrace{xyz} xyz \underbrace{xyz}
|{z}
Algunas flechas
\leftarrow ( \Leftarrow ! \rightarrow
) \Rightarrow \longleftarrow (= \Longleftarrow
! \longrightarrow =) \Longrightarrow $ \leftrightarrow
, \Leftrightarrow 7! \mapsto ! \longleftrightarrow
() \Longleftrightarrow " \uparrow # \downarrow
Algunas funciones
sin \sin cos \cos arcsin \arcsin arc cos \arccos
tan \tan arctan \arctan exp \exp log \log
ln \ln máx \max mı́n \min lı́m \lim
sup \sup ı́nf \inf det \det arg \arg
Otros signos
.. ..
... \ldots ··· \cdots . \vdots . \ddots
@ \aleph 9 \exists 8 \forall 1 \infty
; \emptyset ¬ \neg > \top ? \bot
\ \backslash @ \partial $ \$ % \%
Disponéis de diversos tipos de letra para usar en fórmulas matemáticas; las más útiles
son:
3
• Caligráfica, que se indica con \mathcal{...}; así, por ejemplo, \mathcal{A} y
\mathcal{S} producen A y S, respectivamente. Este tipo sólo se puede usar en
mayúsculas.
p
\sqrt produce raíces cuadradas o de orden superior: \sqrt{xyz} produce xyz mientras
p
que \sqrt[n]{xyz} produce n xyz.
\frac produce fracciones; su tamaño y composición depende de si la fórmula ha de apa-
recer en el interior de un párrafo o ha de aparecer en línea aparte: $\frac{abc}{xyz}$ y
$$\frac{abc}{xyz}$$ producen, respectivamente, xyz abc
y
abc
xyz
P Q
Los operadores , etc. también tienen dos versiones, según se escriban entre dóla-
res o entre dobles dólares; por
P ejemplo $\sum_{i=1}ˆn x_i$ y $$\sum_{i=1}ˆn x_i$$
producen, respectivamente, ni=1 xi y
n
X
xi
i=1
Podemos incluir matrices, o, más en general, tablas, en las fórmulas matemáticas. Una
tabla se define empezando con \begin{array}{formato} y acabando con \end{array}.
El formato es una secuencia de letras l (de izquierda, left), r (de derecha, right) o c (de
centrada): el número de letras indica el número de columnas, y cada letra indica el tipo
de alineamiento de la columna correspondiente. Así, por ejemplo, \begin{array}{rccl}
define una tabla de cuatro columnas: la primera alineada a la derecha, la segunda y la
tercera centradas, y la cuarta alineada a la izquierda.
Entre el \begin{array}{...} y el \end{array} se introducen por filas los valores de la
tabla: los elementos de cada fila se separan con el signo & y el cambio de fila se indica con
\\.
Para definir una matriz, hemos de envolver la tabla con los delimitadores \left( y
\right). Por ejemplo,
$$
\left(\begin{array}{ccc}
a_{1,1} & a_{1,2} & a_{1,3}\\
a_{2,1} & a_{2,2} & a_{2,3}
\end{array}\right)
$$
4
produce la matriz (en línea aparte)
✓ ◆
a1,1 a1,2 a1,3
a2,1 a2,2 a2,3
De manera similar, para indicar el determinante de una matriz, hemos de usar los deli-
mitadores \left| y \right|.
Dos cuestiones a tener en cuenta:
2x + 3y = 5
6x 2y = 8
5
2. Parámetros de los chunks de R
Los bloques de código de R, o chunks, se indican dentro de un documento R Markdown de la
manera siguiente:
```{r}
x=1+1
x
```
Para crear un bloque de código, podemos usar la opción «Insert Chunk» del menú desplegable
«Chunks» en la esquina superior derecha de la ventana de ficheros. Si no queréis usar ese menú,
tened en cuenta que cada signo ` se produce con un acento grave seguido de un espacio en
blanco.
La parte entre llaves que empieza por r puede contener diversos parámetros. En primer lugar,
se puede incluir una etiqueta, distinta para cada bloque. Estas etiquetas han de ser cadenas
de caracteres sin espacios en blanco ni puntos, y se han de separar de la r por medio de un
espacio en blanco, y son útiles para navegar entre bloques con la opción «Jump To» del menú
«Chunks».
A continuación, y separadas por comas entre ellas y de la etiqueta (o de la r, si no hay
etiqueta) se pueden especificar algunas opciones que sirven para determinar el comportamiento
del bloque al compilar el documento pulsando el botón «Knit». Las opciones disponibles, y sus
posibles valores, se os mostrarán en un menú desplegable cuando escribáis la coma; las más
útiles son:
eval, que permite indicar si queremos que se evalúe el código: sus valores son TRUE, que
es el valor por defecto, y FALSE, que hace que no se evalúe.
results, que permite indicar cómo queremos ver el resultado de la ejecución del código
en el documento final: sus posibles valores son palabras, y por lo tanto se han de entrar
entre comillas. Los más útiles son:
6
message, que permite indicar si se han de mostrar en el documento final los mensajes que
R produce al ejecutar el código: sus valores son TRUE, que es el valor por defecto, y FALSE.
warning, que permite indicar si se han de mostrar en el documento final los mensajes
de advertencia que a veces producen algunas funciones al ejecutarse: sus valores son de
nuevo TRUE, su valor por defecto, y FALSE.
El bloque
` ` ` {r , echo = TRUE , results = " markup "}
x=1:10
x
sqrt ( x )
```
## [1] 1 2 3 4 5 6 7 8 9 10
sqrt ( x )
El bloque
` ` ` {r , echo = TRUE , results = " asis "}
x=1:10
x
sqrt ( x )
```
[1] 1 2 3 4 5 6 7 8 9 10
sqrt ( x )
[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 3.000000
3.162278
El bloque
7
` ` ` {r , echo = TRUE , results = " hold "}
x=1:10
x
sqrt ( x )
```
## [1] 1 2 3 4 5 6 7 8 9 10
## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490
## [7] 2.645751 2.828427 3.000000 3.162278
El bloque
` ` ` {r , echo = FALSE }
x=1:10
x
sqrt ( x )
```
## [1] 1 2 3 4 5 6 7 8 9 10
El bloque
` ` ` {r , echo = TRUE , message = TRUE }
library ( magic )
magic ( 5 )
```
8
El bloque
` ` ` {r , echo = TRUE , message = FALSE }
library ( magic )
magic ( 5 )
```
Veamos otro ejemplo más práctico. Supongamos que nos dan una muestra y queremos calcular
su media, su varianza, su desviación típica y su tamaño muestral. Entonces, podemos cargar
los datos y efectuar los cálculos en un bloque de código (que, si queremos, podemos ocultar
completamente en el documento final) y a continuación en un párrafo ir llamando los resultados
mediante pequeños bloques. Por ejemplo, podríamos usar el bloque
` ` ` {r , echo = FALSE , result = " hide "}
muestra = c ( 1 ,2 ,3 ,NA , 2 . 8 ,3 . 1 ,4 . 9 )
media = mean ( muestra , na . rm = TRUE )
n = length ( na . omit ( muestra ) )
varianza = round ( var ( muestra , na . rm = TRUE ) * (n - 1 ) /n , 3 )
desv . tipica = round ( sqrt ( varianza ) ,3 )
```
9
La muestra es de tamaño $n=$ `r n`, su media es $\overline{x}=$
`r media`, su varianza es $sˆ2=$ `r varianza` y su desviación típica
es $s=$ `r desv.tipica`.
Entonces, en el documento final, el bloque con los cálculos permanecería oculto, y este párrafo
produciría
4. Figuras
En el curso se estudian varias funciones que producen gráficos, empezando por la función básica
plot. En un fichero R markdown, podemos controlar el tamaño, la posición etc. de los gráficos
en el documento final (html, pdf o Word) por medio de opciones en el encabezamiento de sus
chunks. Veamos algunas de las opciones más útiles para el nivel básico de este curso:
fig.show sirve para controlar cómo se incluyen en el documento final los gráficos produ-
cidos en el chunk cuando hay más de uno. Su valor por defecto es "as.is", que los va
incluyendo a medida que se generan. Con fig.show="hold" se dibujan todos los gráficos
de golpe al final del chunk.
fig.align permite alinear los gráficos respecto del texto. Sus posibles valores son left,
right y center (a la izquierda, a la derecha y centrados, respectivamente). Si no se
10
especifica, no se efectúa ningún alineamiento específico y se adapta al alineamiento del
texto que rodea el chunk.
dev sirve para especificar el tipo de fichero gráfico que se genera. Solo hay un caso en que
nos parece útil usar esta opción: si pensáis publicar vuestro documento html como una
página web, os conviene usar dev="svg" y el gráfico se creará en formato «Scalable Vector
Graphics», con lo que se escalará de manera correcta y sin perder calidad al aumentar o
disminuir el ancho de la página web en el navegador.
Para más opciones, podéis consultar la Guía de Referencia de R markdown, que encontraréis en
el url http://www.rstudio.com/wp-content/uploads/2015/03/rmarkdown-reference.pdf
y en el repositorio de ficheros del curso, o la página web http://yihui.name/knitr/options/.
Por otro lado, dentro de un chunk podemos usar las funciones par y layout, que también
aparecen en algunas lecciones del curso. Estas funciones sirven para modificar el aspecto de
los gráficos generados con R, y en particular pueden usarse para especificar el modo como se
agrupan los gráficos producidos en un mismo chunk. Su efecto es el mismo al usarlas en la
consola, salvo que allí los cambios introducidos con estas funciones son permanentes hasta que
volvamos a cambiarlos, mientras que en un documento R markdown solo afectan a su chunk.
Con la función
par(mfrow=c(x, y))
organizaremos los gráficos producidos en el chunk formando una matriz de x filas e y columnas,
llenando sus entradas por filas. A modo de ejemplo, si un chunk produce 3 gráficos y que-
remos mostrarlos juntos, con la opción fig.show="hold" aparecerán al final del chunk, uno
debajo del otro. En cambio, si no especificamos la opción fig.show y al principio del chunk
entramos par(mfrow=c(1,3)), se dibujarán uno al lado del otro formando un único gráfico (y
entonces conviene ajustar con fig.height, fig.width o fig.asp sus dimensiones: por ejemplo,
seguramente no querremos que este gráfico compuesto sea cuadrado).
Esta misma construcción se puede usar en la consola, pero entonces hay que tener en cuenta
que a partir de la instrucción par(mfrow=c(x, y)) todos los gráficos se agruparán de esta
manera. Para evitarlo, entrad par(mfrow=c(1,1)) cuando queráis volver al modo por defecto
de los gráficos uno a uno. Veamos un ejemplo:
> par ( mfrow = c (2 ,2) )
> plot ( iris [ iris $ Species == " setosa " ,1:2] , main = " S é palos de setosa " ,
xlab = " Longitud " , ylab = " Anchura " , pch =20)
> plot ( iris [ iris $ Species == " versicolor " ,1:2] , main = " S é palos de
versicolor " , xlab = " Longitud " , ylab = " Anchura " , pch =20 , col = " red " )
> plot ( iris [ iris $ Species == " virginica " ,1:2] , main = " S é palos de virginica " ,
xlab = " Longitud " , ylab = " Anchura " , pch =20 , col = " blue " )
> plot ( iris [ 1:2] , main = " S é palos de iris " , xlab = " Longitud " ,
ylab = " Anchura " , pch =20 , col = " green " )
> par ( mfrow = c (1 ,1) )
produce la Figura 1 y restaura el formato original. En general, con la función par se pueden
modificar «hasta nueva orden» muchos aspectos de los gráficos. Si lo hacéis, es conveniente
que antes de modificarlos guardéis su valor actual con, por ejemplo, par.anterior=par(). A
continuación, ya podéis modificar los parámetros de par que queráis; si en algún momento
11
queréis volver al estilo original, bastará que entréis par(par.anterior).
3.2
4.0
Anchura
Anchura
3.5
2.8
3.0
2.4
2.5
2.0
4.5 5.0 5.5 5.0 5.5 6.0 6.5 7.0
Longitud Longitud
4.0
3.5
3.5
Anchura
Anchura
3.0
3.0
2.5
2.5
2.0
5.0 5.5 6.0 6.5 7.0 7.5 8.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0
Longitud Longitud
M es una matriz que indica la composición en forma de cuadrícula de los gráficos que
queremos agrupar en una misma figura. Sus entradas han de ser 0, 1, 2, . . . , n donde n el
número de gráficos que queremos organizar (el 0 puede faltar, pero no podemos saltarnos
ninguna entre 1 y n). Entonces, si la entrada (i, j) de la matriz M es el número k > 1,
el k-ésimo gráfico producido en el chunk se situará en la posición (i, j) de la cuadrícula;
si la entrada (i, j) de la matriz es 0, la posición (i, j) de la cuadrícula quedará vacía. Si
un valor se repite en diferentes entradas de M , el gráfico correspondiente se reparte entre
estas entradas.
El parámetro widths sirve para especificar las anchuras relativas de las diferentes colum-
nas (por defecto, todas iguales): para ello, se iguala al vector de estas anchuras.
El parámetro heights sirve para especificar, de manera similar, las alturas relativas de
las diferentes filas; por defecto, todas son iguales.
Así, el código
12
> M = matrix ( c (1 ,1 ,1 ,2 ,3 ,4) , nrow =2 , byrow = TRUE )
> M
[ ,1] [ ,2] [ ,3]
[1 ,] 1 1 1
[2 ,] 2 3 4
> layout ( M )
> plot ( iris [ 1:2] , main = " S é palos de iris " , xlab = " Longitud " ,
ylab = " Anchura " , pch =20 , col = " green " )
> plot ( iris [ iris $ Species == " setosa " ,1:2] , main = " S é palos de setosa " ,
xlab = " Longitud " , ylab = " Anchura " , pch =20)
> plot ( iris [ iris $ Species == " versicolor " ,1:2] , main = " S é palos de
versicolor " , xlab = " Longitud " , ylab = " Anchura " , pch =20 , col = " red " )
> plot ( iris [ iris $ Species == " virginica " ,1:2] , main = " S é palos de virginica " ,
xlab = " Longitud " , ylab = " Anchura " , pch =20 , col = " blue " )
> layout (1)
produce la Figura 2, que no es ninguna maravilla, pero sirve para observar cómo la matriz
M organiza los gráficos: el primer plot ocupa toda la primera fila, y los otros tres, las tres
posiciones de la segunda fila ordenados de izquierda a derecha. Como antes, el efecto de layout
es permanente, por lo que si queréis volver al modo «gráficos de uno en uno» lo más práctico es
que entréis al terminar vuestro gráfico compuesto la instrucción layout(1), como hemos hecho
en el código anterior. Encontraréis otro ejemplo del uso de layout al final de la Lección 11.
Sépalos de iris
4.0
3.5
Anchura
3.0
2.5
2.0
Longitud
3.5
4.0
3.0
3.5
2.8
Anchura
Anchura
Anchura
3.0
2.6
3.0
2.4
2.5
2.2
2.5
2.0
4.5 5.0 5.5 5.0 5.5 6.0 6.5 7.0 5.0 6.0 7.0 8.0
13
5. Tablas
La manera más sencilla de incluir tablas producidas con R en un fichero R markdown es usando
el paquete printr. A día de hoy (mayo 2016) aún no ha sido incorporado al repositorio de la
CRAN, y para instalar su última versión hay que ejecutar en la consola
> install . packages ( " printr " , type = " source " ,
repos = c ( " http : / / yihui . name / xran " ) )
Una vez instalado, para usarlo en un fichero R markdown hay que cargarlo con library en un
chunk al principio; recordad que para usar un paquete en un fichero R markdown no basta que
esté cargado en la sesión de la consola, se tiene que cargar específicamente en el fichero.
Este paquete hace que las matrices, tablas de contingencia y data frames se presenten como
tablas de manera adecuada, que además dependerá del formato de salida elegido. Veamos
algunos ejemplos de chunks y cómo se ven sus resultados. Para empezar, veamos como muestra
los data frames en el documento final. El chunk
```{r}
library ( printr )
head ( iris )
```
14
(a)
(b)
(c)
Figura 3. Tabla de las seis primeras filas de iris producidas
con el paquete printr: (a) en formato html; (b) en formato
pdf; (c) en formato Word.
M = matrix ( c ( 2 ,8 ,5 ,5 , 9 ,7 , 3 ,6 ,6 ,5 , 2 ,7 , 3 ,7 ,7 , 8 ,9 ,6 , 9 ,3 ,2 ,
3 ,3 ,1 0 ,8 ,1 0 ,2 ,1 0 ,8 ,7 , 3 ,3 ,2 ,6 , 3 ,2 ,1 ,9 ,8 ,5) , nrow = 8 )
M
dimnames ( M ) = list ( NULL , paste ( " Col " ,1 :5 , sep = " . " ) )
M
```
produce, con la primera M , la tabla (a) de la Figura 5 y con la segunda, con nombres en las
columnas, la (b).
Si en algún momento queréis volver a la presentación usual de matrices, data frames y tablas
de contingencia, basta que descarguéis el paquete printr entrando en un chunk
detach("package:printr", unload=TRUE)
y luego ya lo volveréis a cargar si lo necesitáis de nuevo. Así, con
```{r}
detach ( " package : printr " , unload = TRUE )
M
```
15
(a)
(b)
(c)
Figura 4. Tablas de contingencia producidas en formato pdf
con el paquete printr: (a) unidimensional; (b) bidimensional;
(c) tridimensional.
obtenemos
## Col . 1 Col .2 Col .3 Col .4 Col . 5
## [ 1 ,] 2 6 9 8 2
## [2 ,] 8 5 6 10 6
## [3 ,] 5 2 9 2 3
## [4 ,] 5 7 3 10 2
## [ 5 ,] 9 3 2 8 1
## [6 ,] 7 7 3 7 9
## [7 ,] 3 7 3 3 8
## [8 ,] 6 8 10 3 5
16
(a) (b)
Figura 5. Matrices producidas en formato pdf con el paquete
printr: (a) sin nombres de columnas; (b) con nombres de
columnas.
17
Matemàtiques I dels graus de Biologia i Bioquímica.
Distribucions de probabilitats amb R
R coneix les distribucions de probabilitat més importants. La Taula 1 dóna algunes d’aquestes
distribucions. En aquesta taula donam els paràmetres de la distribució, en l’ordre que s’hi han
d’entrar. Si voleu més informació sobre cada distribució, inclosos altres paràmetres, demanau-la
amb el help aplicat a qualsevol de les quatre funcions associades a la distribució que explicam tot
seguit.
Per a cada una d’aquestes distribucions, R en sap calcular quatre funcions, que s’obtenen afegint
un prefix al nom de la distribució: la funció de densitat (afegint-hi el prefix d), la funció de distribució
de probabilitat (afegint-hi el prefix p), els quantils (afegint-hi el prefix q) i llistes de nombres aleatoris
generats amb aquesta distribució (afegint-hi el prefix r). La funció corresponent s’aplica aleshores
al valor al qual volem aplicar la funció i als paràmetres de la distribució (en aquest ordre, i els
paràmetres en l’ordre en què els donam a la Taula 1, quan n’hi ha més d’un).
Per exemple, suposem que treballam amb una binomial de mida n = 20 i probabilitat p = 0.3.
Diguem, per simplificar, f a la seva funció de densitat i F a la seva funció de distribució.
1
[1] 6 7 8 3 4 5 3 4 7 4
De la mateixa manera, si estam treballant amb una Poisson de paràmetre = 5, funció de densitat
f i funció de distribució F , tenim
Si no entram cap paràmetre a la distribució normal, R entén que es tracta de la normal estàndard
(amb mitjana µ = 0 i desviació típica = 1):
> dnorm(0.3)
[1] 0.3813878
> dnorm(0.3,0,1)
[1] 0.3813878
Les funcions densitat i distribució d’una variable aleatòria es poden dibuixar o integrar. Fixau-vos
en la sintaxi:
> curve(dnorm(x,0,1.5),-5,5)
> curve(pnorm(x,0,1.5),-5,5)
Model de test
(1) Sigui f la funció de densitat d’una variable aleatòria amb distribució normal amb µ = 0.2 i
= 1.2. Donau el valor de f (0.5) arrodonit a 4 xifres decimals.
(2) Sigui X una variable aleatòria amb distribució normal amb µ = 0.2 i = 1.2. Donau el valor
de P (3 6 X 6 7) arrodonit a 4 xifres decimals.
(3) Sigui X una variable aleatòria amb una distribució B(10, 0.2). Donau el valor de P (3 6 X 6 7)
arrodonit a 4 xifres decimals.
(4) Donau una instrucció que calculi la mediana d’una llista de 20 nombres aleatoris generats amb
una distribució B(10, 0.2). No doneu el resultat.
Respostes
2
Campus Extens
UIB Virtual
http://campusextens.uib.cat
@campusextensUIB
http://www.scoop.it/t/recursos-i-eines-per-al-professorat
http://campusextensrecursos.uib.es/