Introducción a R Juan Pablo Pérez Grijalbo 9 de febrero de 2007 2 Histograma(velocidad) Dispersión(vel/dist) −1 0 1 2 3 0 5 10 15 20 25 5 10 15 20 25 velocidad velocidad Histograma(distancia) Dispersión(dist/vel) 15 velocidad 10 10 0 0.0 5 0.1 5 0.2 frecuencia 0.3 20 0.4 15 25 0.5 Densidad(distancia) densidad 60 80 −3 0 0 0.0 20 0.1 5 40 distancia 10 frecuencia 0.3 0.2 densidad 0.4 100 15 0.5 120 Densidad(velocidad) −2 0 1 2 3 4 0 20 40 60 80 distancia 120 0 20 40 60 80 distancia 120 Índice de figuras 3.1. Manual de R en html . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Hacer histogramas con hist() . . . . . . . . . . . . . . . . . . . . . . 3.3. Diagrama de dispersión con la función plot . . . . . . . . . . . . . . 17 20 20 5.1. Diagrama de caja con la función boxplot() . . . . . . . . . . . 5.2. Varios diagramas de caja con la función boxplot() . . . . . . . 5.3. Diagramas de caja de anchura variable con el argumento width 5.4. Modificación de las clases del histograma con breaks . . . . . 5.5. Frecuencias relativas con freq=F . . . . . . . . . . . . . . . . 5.6. Histograma con la función hist() . . . . . . . . . . . . . . . . 5.7. Función de densidad con plot(density()) . . . . . . . . . . . . 5.8. Gráfico Q-Q con la función qqnorm() . . . . . . . . . . . . . 5.9. Otro gráfico Q-Q con la función qqnorm . . . . . . . . . . . . 5.10. Gráfico de barras con barplot() . . . . . . . . . . . . . . . . . 5.11. Un gráfico interesante para tablas bidimensionales . . . . . . . 5.12. Representación gráfica de una tabla multidimensional . . . . . 5.13. Representación de parte de una tabla multidimensional . . . . . . . . . . . . . . . . . 39 39 41 41 42 43 43 45 45 54 54 55 56 6.1. Notched boxplots . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 7.1. Un ejemplo de escasa correlación . . . . . . . . . . . . . . . . . . . . 7.2. En este caso, la correlación es fuerte . . . . . . . . . . . . . . . . . . 7.3. Diagrama de dispersión con dos grupos de datos . . . . . . . . . . . . 7.4. Diagrama mucho más elaborado . . . . . . . . . . . . . . . . . . . . 7.5. Diagrama de dispersión con dos grupos de datos obtenido con coplot() 7.6. Diagrama de dispersión controlando para dos variables . . . . . . . . 7.7. Diagrama de dispersión cruzada entre variables con pairs() . . . . . . 7.8. Relación entre temperatura y distancia . . . . . . . . . . . . . . . . . 7.9. Relación entre temperatura y flujo térmico . . . . . . . . . . . . . . . 7.10. Relación entre las variables de trees . . . . . . . . . . . . . . . . . . 7.11. Inclusión de la recta de regresión con abline() . . . . . . . . . . . . . 7.12. Identificación de casos con identify() . . . . . . . . . . . . . . . . . . 66 66 68 68 69 69 71 75 75 77 79 80 8.1. Personalización de títulos . . . . . . . . . . . . . . . . . . . . . . . . 83 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 ÍNDICE DE FIGURAS 8.2. Ejes logarítmicos . . . . . . . . . . . . . . . . . . . . . . . . . . 8.3. Modificar etiquetas de ejes con axis() . . . . . . . . . . . . . . . 8.4. Millones de colores con rgb() y hsv() . . . . . . . . . . . . . . . . 8.5. Añadir textos con text() . . . . . . . . . . . . . . . . . . . . . . . 8.6. Utilizar funciones para establecer coordenadas de textos con text() 8.7. Personalización de líneas con lty,lwd,col . . . . . . . . . . . . . . 8.8. Personalización de puntos con pch,cex,col . . . . . . . . . . . . . 8.9. Añadir puntos con points() . . . . . . . . . . . . . . . . . . . . . 8.10. Añadir lineas con abline() . . . . . . . . . . . . . . . . . . . . . 8.11. Añadir curvas con curve() . . . . . . . . . . . . . . . . . . . . . . 8.12. Modificación de parámetros gráficos con par() (I) . . . . . . . . . 8.13. Modificación de parámetros gráficos con par() (II) . . . . . . . . 8.14. Modificación de parámetros gráficos con par() (III) . . . . . . . . 8.15. División de la pantalla gráfica con layout() . . . . . . . . . . . . . 8.16. División desigual de la pantalla gráfica con layout() . . . . . . . . 8.17. Dos histogramas con layout() . . . . . . . . . . . . . . . . . . . . 8.18. Figuras múltiples par(mfrow) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 85 85 86 87 87 89 89 90 91 92 92 93 94 94 96 97 Índice general 1. Introducción 1.1. ¿Por qué escribo este libro? . . . . . . . . . . . . . . . . . . . . . . . 1.2. Características principales de R . . . . . . . . . . . . . . . . . . . . . 1.3. Algunas cuestiones sobre el texto . . . . . . . . . . . . . . . . . . . . 7 7 8 9 2. Obtención e instalación 2.1. Instalación en Linux . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. Instalación en Windows . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 12 3. Primeros pasos 3.1. Iniciar una sesión de trabajo 3.2. Cómo pedir ayuda . . . . . . 3.3. Manos a la obra . . . . . . . 3.4. Primera sesión . . . . . . . . 3.5. Acabar una sesión de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 13 14 16 17 21 4. Gestión de objetos y ficheros 4.1. Introducción de datos en R . . . . . . . . . . . . . 4.1.1. Importación de tablas externas: read.table() 4.1.2. Edición de datos en R . . . . . . . . . . . 4.2. Salida de información de R . . . . . . . . . . . . . 4.2.1. Exportación de gráficos . . . . . . . . . . . 4.2.2. Exportación de textos . . . . . . . . . . . . 4.3. Manipulación de data.frames . . . . . . . . . . . . 4.3.1. Selección de variables . . . . . . . . . . . 4.3.2. Extracción de variables . . . . . . . . . . . 4.3.3. Selección de casos . . . . . . . . . . . . . 4.3.4. Extracción de casos . . . . . . . . . . . . . 4.3.5. Introducción de variables . . . . . . . . . . 4.3.6. Incorporación de registros . . . . . . . . . 4.3.7. Ordenación de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 23 23 25 26 26 27 27 28 28 29 30 31 33 34 . . . . . . . . . . . . . . . 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ÍNDICE GENERAL 6 5. Estadística descriptiva 35 5.1. Análisis exploratorio de los datos . . . . . . . . . . . . . . . . . . . . 35 5.1.1. Medidas características . . . . . . . . . . . . . . . . . . . . . 35 5.1.2. El gráfico stem and leaf . . . . . . . . . . . . . . . . . . . . . 37 5.1.3. El gráfico boxplot . . . . . . . . . . . . . . . . . . . . . . . . 38 5.1.4. El histograma . . . . . . . . . . . . . . . . . . . . . . . . . . 40 5.1.5. Contraste de normalidad . . . . . . . . . . . . . . . . . . . . 40 5.2. Cómo resumir la información contenida en un data.frame . . . . . . 46 5.2.1. Tablas de frecuencias absolutas . . . . . . . . . . . . . . . . 46 5.2.2. Tablas con otras medidas estadísticas . . . . . . . . . . . . . 50 5.2.3. Métodos gráficos para presentar la información contenida en tablas 53 6. Pruebas de significación 6.1. Pruebas no paramétricas . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1. Pruebas basadas en la χ2 . . . . . . . . . . . . . . . . . . . . 6.1.2. Test de Wilcoxon, de Mann-Whitney y de Kolmogorov-Smirnov 6.1.3. Test de Kruskal-Wallis . . . . . . . . . . . . . . . . . . . . . 6.2. Pruebas paramétricas . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2.1. Test basados en la t de Student . . . . . . . . . . . . . . . . . 57 57 57 59 60 62 62 7. Correlación y regresión 7.1. Estudio de la correlación bivariada . . . . . . . 7.1.1. Métodos gráficos . . . . . . . . . . . . 7.1.2. Matrices de correlaciones . . . . . . . 7.1.3. Coeficiente de correlación de Spearman 7.1.4. Coeficiente de correlación parcial . . . 7.2. Regresión lineal simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 65 65 70 72 73 76 8. Personalización de gráficos 8.1. Títulos . . . . . . . . . . . . . . . 8.2. Ejes . . . . . . . . . . . . . . . . 8.3. Colores . . . . . . . . . . . . . . 8.4. Añadir textos a un gráfico . . . . . 8.5. Tipos de puntos y lineas . . . . . . 8.6. Añadir puntos y lineas a un gráfico 8.7. Plantillas gráficas . . . . . . . . . 8.8. Gráficos múltiples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 81 82 84 84 86 88 90 93 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Capítulo 1 Introducción 1.1. ¿Por qué escribo este libro? El objetivo de este documento es proporcionar una base en castellano para aquellos que están interesados, por el motivo que sea, en iniciarse en el uso de R. Cuando uno se entera de la existencia de R, y se siente interesado por él, lo primero que necesita es un poco de documentación porque, realmente, no es un programa que se aprenda a base de trastear. Es entonces cuando se busca por internet. Por lo general, al que está metido en el mundo de la informática, no le queda otro remedio que manejarse con el inglés. Es entonces cuando uno siente cierta sana envidia por esas sociedades que generan tanto conocimiento y, sobre todo, por la gente que tiene un acceso inmediato a esa información. También es cuando uno se explica que les haya ido tan bien a algunas naciones en lo que al desarrollo (al menos científico y tecnológico) se refiere. Mientras tanto, a los demás no nos queda otra opción que intentar descifrar el lenguaje imperante; en general porque nos puede más el deseo de saber que la reticencia natural a aprender un idioma extranjero1 Para los que entienden el inglés hay buenísimos manuales disponibles en la página oficial de R. Se incluyen, además, documentos de tipo introductorio mucho mejores que este2 . Sin embargo, hay gente que presenta esas reticencias al inglés y que, por tanto, no tienen acceso a un entorno estadístico realmente fantástico. Entre esos sectores de población se encuentran especialmente los alumnos de bachillerato e, incluso, de niveles universitarios. En realidad, este documento fue concebido en principio como un material de clase para ser utilizado con mis alumnos y alumnas del primer curso de bachillerato. Decidí entonces redactar un pequeño manual para trabajar con ellos en el aula cuestiones de estadística básica, puesto que, por un lado, era consciente de lo 1 En realidad, existen algunos documentos en castellano, como la traducción de Introducción a R, del equipo de desarrollo de R; y Gráficos estadísticos con R, de J.C. Correa y N. González. Ambos están disponibles en la página web de R. 2 En mi opinión, los mejores son simpleR. Using R for introductory statistics de John Verzani y Using R for data analysis and graphics. An introduction de J.H.Maindonald. Como los anteriores, están disponibles en la página web de R 7 CAPÍTULO 1. INTRODUCCIÓN 8 inadecuado que podría resultar remitirles a la documentación en inglés pero, por otro, deseaba introducirles en el mundo del software libre y darles a conocer R.3 . En fin, espero que estos apuntes sirvan para aquellos hispanoparlantes que se enfrentan por primera vez con R. Para que puedan empezar a descubrir las inmensas posibilidades que ofrece y, quién sabe, llegar a entusiasmarse con él como lo hace el que escribe estas lineas. 1.2. Características principales de R En mi opinión las principales características de R son 1. las operaciones (cálculos estadísticos, gestión de ficheros,. . . ) se realizan tecleando instrucciones en un terminal. No es, por lo tanto, un programa en modo gráfico 2. hay toda una comunidad internetera girando alrededor de él. En la red podemos encontrar actualizaciones, paquetes, listas de correo, manuales,. . . 3. además del sistema base, existen paquetes especializados que permiten aplicar R a campos superespecíficos como la estadística espacial, molecular, social,. . . 4. es un lenguaje de programación, de modo que tenemos la posibilidad de ampliar su funcionalidad en la medida de nuestros conocimientos sobre programación 5. es un sistema multiplataforma, estando disponible, entre otros, para sistemas Linux, Windows y MacOS 6. es software libre, de modo que podemos adaptar el programa a nuestras necesidades ya que se distribuye con el código fuente 7. es gratuito Por supuesto, R tiene ventajas e inconvenientes. También por supuesto, qué son ventajas y qué inconvenientes depende de cada uno, pero (sobretodo) depende de con qué se compare. R es un entorno estadístico y, por lo tanto, debe ser comparado con aplicaciones de este tipo no, por ejemplo, con hojas de cálculo o similares. Para mi son ventajas 1. el no sentirte solo ante el programa. La comunidad R es muy amplia y atenta. Siempre responde a tus dudas 2. tiene unas capacidades gráficas extraordinarias 3. poder utilizarlo tanto en Linux como en Windows 4. poder definir funciones propias 5. tener acceso a funciones de estadística avazada 3 Sin embargo, a medida que lo he ido escribiendo lo he ido ampliando y, en estos momentos, supera las posibilidades de aplicación en esos niveles, por lo que a ellos les paso una versión adaptada 1.3. ALGUNAS CUESTIONES SOBRE EL TEXTO 9 6. trabajar con ficheros fácilmente portables 7. aunque parezca extraño, el trabajar en linea de comandos a la larga resulta una ventaja en muchas ocasiones ya que permite realizar muchas operaciones de forma más rápida que en modo gráfico 8. se actualiza con mucha frecuencia 9. es gratuito, de modo que uno puede responder tranquila y públicamente a la pregunta ¿con qué programa has hecho esto?, cosa que no pueden hacer muchos de los usuarios de otras aplicaciones estadísticas Entre los inconvenientes destacaría 1. al principio, el trabajo en modo texto es incómodo 2. la curva de aprendizaje es, también al principio, algo pendiente, de modo que es necesario material de apoyo para el aprendizaje 3. el lenguaje del programa es el inglés, y también en inglés está la mayor parte de la documentación disponible 4. es un programa poco conocido por el usuario normal de estadística ya que no hay libros de R en las secciones correspondientes de las librerías especializadas 1.3. Algunas cuestiones sobre el texto En el texto encontraremos en negrita todo aquello que debamos introducir por teclado en el terminal para realizar alguna operación en R. Por otro lado, aparecerán en cursiva 1. las salidas por pantalla de las instrucciones que tecleemos 2. los nombres de las funciones y argumentos o parámetros de las mismas 3. los nombres de objetos particulares del espacio de trabajo 4. como sustituto de las comillas allí donde pudieran utilizarse La operación de asignación puede efectuarse con el signo = o con la combinación de signos <-. En el texto se utiliza esta última para ser compatible con versiones anteriores de R. 10 CAPÍTULO 1. INTRODUCCIÓN Capítulo 2 Obtención e instalación La versión 1.6.1 de R, de noviembre de 2002, se te proporciona con este documento en las versiones para Linux y para Windows. Sin embargo, cuando lo leas, casi con toda seguridad habrá alguna versión más nueva, ya que la intención del equipo que desarrolla R es liberar dos versiones al año. Por tanto, si deseas lo último en R tendrás que descargarlo de la página oficial del proyecto CRAN (www.cran.r-project.org). La instalación difiere si eres usuario de Linux o de Windows. 2.1. Instalación en Linux Los usuarios habituales de Linux pueden saltarse este apartado (si no lo han hecho ya) porque no tienen más que obtener R e instalárselo en su ordenador. Si no lo eres, lo que sigue pretende ayudarte a instalártelo. Si utilizas una distribución de Linux completa, tal vez se halle incorporada alguna versión de R en la selección de programas que hayan hecho; en ese caso puedes utilizar para instalar R la aplicación de gestión de paquetes que te venga con la distribución. En cualquier caso, no será la última. Si lo descargas de Internet debes saber que en la página de CRAN vas a encontrar diferentes versiones del programa. Por un lado están los ficheros con el código fuente y, por otro, los archivos con ficheros binarios precompilados para las distribuciones más usuales (Debian, RedHat, Suse y Mandrake). Lo mejor es descargarse el que corresponda a nuestra distribución e instalarlo con la herramienta de gestión de paquetes que tengamos. Otra posibilidad es utilizar el CD que te proporciono. En él te encontrarás con un paquete RPM y varios DEB. Para instalar R desde la linea de comandos sólo tienes que escribir en un terminal rpm -i R-1.6.1-2.i386.rpm lo que instalará R en la mayor parte de las distribuciones. Si tu distribución es Debian o basada en debian, puedes utilizar los archivos DEB, para lo cual deberás escribir dpkg -i r-base-core_1.6.1-1_i386.deb dpkg -i r-recommended_1.6.1-1_all.deb 11 12 CAPÍTULO 2. OBTENCIÓN E INSTALACIÓN dpkg -i r-doc-pdf_1.6.1-1_i386.deb Si todo ha ido bien1 , podrás arrancar R desde el terminal simplemente tecleando R. 2.2. Instalación en Windows Los que utilizan Windows no lo tienen mucho más difícil. Tienen que descargarse el archivo correspondiente que hay en el directorio base del enlace a Windows de la página de descarga. Tras descargarlo sólo hay que hacer doble clic sobre el icono del archivo rw1061.exe e ir superando las pruebas que nos pone el asistente de instalación. Tras concluir y, si así lo queremos, tendremos un icono en el escritorio que nos permitirá lanzar la aplicación. 1 los problemas más habituales son los de dependencias no satisfechas, en cuyo caso deberás instalar previamente los paquetes precisos Capítulo 3 Primeros pasos 3.1. Iniciar una sesión de trabajo Tras arrancar R nos encontramos con un terminal en el que deberemos teclear los comandos necesarios para realizar todas las operaciones que vayamos a realizar; desde la edición y gestión de ficheros de datos, a los análisis estadísticos y generación de gráficos. Como dijimos en la Introducción, deberemos aprender el idioma de R. Sin embargo, y aunque es amplio, veremos que con muy poco esfuerzo podremos empezar a trabajar. Además, una característica de R muy útil es que los comandos que se introducen por teclado se van almacenando en un historial, de manera que podemos recuperar las órdenes introducidas en un momento anterior con la ayuda de la tecla flecha-arriba. Esto nos ahorrará mucho trabajo de tecleado y de memorización de la sintaxis de los comandos ya que una vez introducido podremos recuperarlo posteriormente. Por otro lado, esta facultad nos va a permitir realizar análisis estadísticos de una manera incluso más rápida de lo que lo haríamos con una aplicación en modo gráfico, en la que deberíamos desplazarnos por diferentes ventanas para realizar un análisis prácticamente idéntico al realizado en un momento anterior. En R nos vamos a encontrar con lo que se denomina "espacio de trabajo"(workspace en inglés). Es una entidad virtual en la que se van a ir almacenando las variables y análisis que vayamos haciendo a lo largo de nuestra sesión de trabajo. En Linux, el espacio de trabajo se almacena en el directorio desde el que se ha lanzado R, de modo que al arrancar R se cargará el espacio de trabajo correspondiente al directorio en el que nos encontremos al lanzar la aplicación. Por ese motivo, antes de comenzar un estudio estadístico (digamos, Clima de Aragón) es conveniente crear un directorio (tal vez, Clima de Aragón). Cuando vayamos a trabajar sobre este asunto acudiremos a ese directorio para, desde allí, arrancar R. Todos los ficheros que vayamos generando (de texto, gráficos,...) en la sesión de R se irán guardando por defecto en este mismo directorio. Si no hacemos esto y arrancamos R, sea cual sea el problema estadístico, desde un directorio cualquiera, por ejemplo home, iremos acumulando objetos de diferentes problemas estadísticos en el mismo espacio de trabajo, lo que acabará creando la consi13 14 CAPÍTULO 3. PRIMEROS PASOS guiente confusión. Si analizamos los ficheros ocultos contenidos en el directorio de trabajo, vemos que hay uno llamado .Rhistory que es el que va almacenando el histórico de comandos introducidos en R. Como es un fichero de texto, podemos visualizarlo con cualquier editor. En la versión de R para Windows las cosas son algo distintas porque, aunque el espacio de trabajo también se guardará en el directorio desde el que se lance la aplicación, lo cierto es que la operación de arranque se suele hacer, bien con un doble clic sobre el icono colocado en el escritorio o la barra de programas, bien desplegando el menú de Inicio hasta llegar a la aplicación. Una vez arrancado R, para saber cuál es el directorio de arranque deberemos teclear getwd() y anotar la ruta de acceso que nos indica por pantalla que, si hemos hecho una instalación de Aceptar en Aceptar, estará en C:\Archivos de programa\R\rw1051 . Cuando hayamos acabado la sesión de trabajo con R y guardado el espacio de trabajo, nos encontraremos en el directorio que nos haya indicado getwd() con un icono en forma de R. Es conveniente entonces poner un nombre a ese icono (que de momento no tiene ninguno) y trasladarlo a la carpeta en la que vayamos a guardar todo el material que vayamos generando en nuestro trabajo estadístico. Si este procedimiento nos parece demasiado complicado, podemos utilizar el comando /File/Save Workspace, que nos permite guardar una imagen de nuestro espacio de trabajo en el directorio que deseemos. Tras acabar nuestra sesión de trabajo, deberemos descartar los cambios si queremos que, al abrir de nuevo R desde el icono de la aplicación, no se cargue ningún workspace. A partir de ese momento, cuando queramos recuperar ese espacio de trabajo en concreto, tendremos dos opciones: 1. lanzar R haciendo doble clic sobre el icono situado en la carpeta de trabajo 2. hacer sobre doble clic sobre el icono que abre la aplicación y cargar el espacio de trabajo desde File/Load workspace. Nos aparecerá una ventana en la que sólo deberemos indicar la carpeta en la que hemos guardado la imagen del espacio de trabajo. Cuando arrancamos R desde el icono de la aplicación, se recupera el último espacio de trabajo abierto, que puede ser,o no, el que nos interese a nosotros. La mejor manera de averiguarlo es tecleando ls(); si se ha cargado el que deseamos, es obvio que no es preciso hacer la operación anterior. En la versión de R para Windows disponemos de una ventana con menús, una pequeña barra de herramientas y la consola para introducir los comandos por teclado. Aunque no hay procedimientos estadísticos en modo gráfico, sí que hay algunos comandos útiles, como el que permite la importación de paquetes desde el sitio web de CRAN o la personalización del entorno. 3.2. Cómo pedir ayuda R dispone de un sistema de ayuda muy eficaz que nos va a permitir aprender el uso de los comandos con bastante rapidez. Si conocemos el nombre del comando sobre el 3.2. CÓMO PEDIR AYUDA 15 que deseamos ayuda (p.e. plot) podemos utilizar la función help(). Lo único que debemos teclear es help(plot) o ?plot. En ese momento, si trabajamos con la versión de Linux, el terminal abandona entorno de R en el que estamos para arrancar el visualizador de la ayuda. El desplazamiento por la ayuda se realiza con las teclas de arriba-abajo y de Av.Pag-Re.Pag. Todas las ayudas suelen acabar con ejemplos de utilización de la función. Para salir de ella sólo hay que pulsar la tecla q. Existe la posibilidad de visualizar la ayuda con un navegador web. Para ello, en el caso anterior se debería teclear help(plot, htmlhelp=T) con lo que arrancaría el navegador Mozilla. En la página web aparecerá el mismo contenido de la ayuda pero con sus ventajas en cuanto a navegación. Además, en este caso, no se pierde en ningún momento el controlde edición de comandos en el prompt de R. El problema viene cuando no conocemos el nombre de los comandos, pero, ni siquiera en ese caso está todo perdido. Entre otras cosas, tenemos manuales (como este) que nos van presentando algunos de los comandos más habituales. Sólo es cuestión de empezar a recordarlos. Además, existen varias posibilidades de ayuda desde R. Si tecleamos help.start(), arrancará el navegador web en una página de inicio que presenta enlaces a diferentes documentos. En este momento nos interesa especialmente el enlace a los paquetes de R (Packages). De este modo podremos obtener una relación de todo lo que contiene un paquete determinado. De entrada nos viene el nombre del comando a teclear y una pequeña descripción de lo que hace (si es un procedimiento) o de lo que contiene (si es un fichero de datos). Aunque tiene infinidad de funciones, es conveniente echar un vistazo al paquete Base porque podremos ir reconociendo las que utilizaremos con más frecuencia. Si tenemos alguna idea de cómo puede llamarse la función que queremos emplear podemos utilizar la función apropos(). Por ejemplo, si quiero hacer una media ponderada pero no sé el nombre exacto de la función que me permita obtenerla, puedo teclear apropos(mean) cuya salida sería [1]“mean” “mean.POSIXc” “mean.POSIXl” “mean.default” [5] “weighted.mean” Podemos ver entonces que existe una función llamada weighted.mean(). El siguiente paso sería solicitar ayuda sobre la misma con help(weighted.mean) para aprender a utilizarla. Otra herramienta muy útil para pedir ayuda en R es la función help.search(). Esta nos devuelve todas las entradas de ayuda que presenten en sus tilulos o palabras clave la cadena de caracteres introducida como argumento de la función. Así, help.search(“help”) devuelve example(base) Run an Examples Section from the Online Help help(base) Documentation help.search(base) Search the Help System help.start(base) Hypertext Documentation index.search(base) Search Indices for Help Files Cuando apropos(help) devuelve 16 CAPÍTULO 3. PRIMEROS PASOS [1]“help” “help.search” “help.start” Otra función muy útil es methods(), que permite conocer diferentes posibilidades de uso de procedimientos utilizados en R. Por ejemplo, si nos interesa conocer variantes de uso de la función lines(), podemos teclear methods(lines) que nos proporciona la siguiente salida [1] “lines.default” “lines.formula” “lines.histogram” “lines.ts”. Tambiém podemos aplicarlo a una clase de objetos, como hacemos en el siguiente ejemplo con las tablas methods(class=table) [1] “pairwise.table” “as.data.frame.table” “as.table” [4] “is.table” “margin.table” “plot.table” [7] “print.summary.table” “int.table” “prop.table” [10] “read.table” “summary.table” “write.table” Posteriormente, con las funciones normales de ayuda, podemos entrar a estudiar el funcionamiento de aquellas que nos puedan interesar. En Windows encontramos en la pantalla un menú de ayuda. Allí nos encontramos con accesos a las FAQ’s, al manual de R y otras fuentes de documentación. Éstos están en formato PDF, pero la versión en HTML podemos lanzarla desde la línea de comandos con la función help.start(). La captura de pantalla de la Figura 3.1 se ha obtenido de esa manera. 3.3. Manos a la obra Antes de ponernos a trabajar con nuestros datos, es conveniente echar un vistazo a alguno de los datos que vienen incorporados con R. Si tecleamos data(), aparecerá en pantalla una relación de ficheros de datos que podremos cargar en nuestro espacio de trabajo. Para cargar uno en concreto, debe incluirse como argumento de la función. Así, data(cars) cargaría el fichero cars. Para ver qué objetos tenemos en nuestro espacio de trabajo hay que utilizar la función ls() sin argumentos. En este caso el único objeto debe ser el recién cargado fichero cars. Para ver el contenido de un objeto de datos tenemos dos posibilidades: la primera es llamarlo directamente por su nombre. En nuestro caso, si tecleamos cars, aparecerá por pantalla todo el contenido del fichero. Veremos entonces que consta de cincuenta registros de velocidad y distancia de frenada de otros tantos vehículos. Este modo de exploración no es conveniente cuando el fichero contiene muchas variables y/o registros. En estos casos es mejor utilizar la función str() para hacernos una idea del contenido del objeto. Así, str(cars) generaría la siguiente salida ‘data.frame’: 50 obs. of 2 variables: $ speed: num 4 4 7 7 8 9 10 10 10 11 ... $ dist : num 2 10 4 22 16 10 18 26 34 17 ... La función str() nos informa de que cars es un data.frame, es decir, una tabla de datos en la que podemos encontrarnos variables de diferentes tipos. Nos dice que hay 50 observaciones de dos variables que son de tipo numérico y cuyos nombres son speed y 3.4. PRIMERA SESIÓN 17 Figura 3.1: Manual de R en html dist. Los data.frame son un tipo de objeto de importancia capital en R ya que en la mayor parte de los análisis estadísticos los datos serán almacenados en data.frames. La función str() es la mejor manera de conocer la estructura de un objeto en R. 3.4. Primera sesión Las estadísticas más básicas que debemos conocer de nuestros datos son las relativas a medidas de tendencia central y de dispersión. La función summary() nos proporciona algunas de ellas. Por ejemplo, summary(cars) nos indica lo siguiente speed Min. : 4.0 1st Qu.:12.0 Median :15.0 Mean :15.4 3rd Qu.:19.0 Max. :25.0 dist Min. : 2.00 1st Qu.: 26.00 Median : 36.00 Mean : 42.98 3rd Qu.: 56.00 Max. :120.00 es decir, proporciona los valores mínimo y máximo de cada variable así como la media aritmética y los tres cuartiles. Como veremos, con summary() podemos conocer algunos estadísticos básicos. Las funciones mean(), median(), max(), min(), sum() y var() aplicadas a una varible proporcionarán por separado la media aritmética, mediana, valor máximo, mínimo, suma CAPÍTULO 3. PRIMEROS PASOS 18 y varianza respectivamente. Sin embargo, si nos limitamos a teclear estas funciones, R sólo presentará el valor del estadístico en la pantalla. Si deseamos almacenar alguno de estos valores para trabajar con ellos posteriormente (o por el motivo que sea) será preciso guardarlo en nuestro espacio de trabajo. Para ello debemos hacer una de las operaciones más básicas en R, que es la asignación. Llamamos asignación al proceso por el cual almacenamos en un objeto el resultado obtenido a través de una función de R. El objeto se incorporará automáticamente al espacio de trabajo. Para llevar a cabo la asignación debemos decidir en primer lugar el nombre que queremos dar al objeto. Una vez hecho esto, podemos pasar a teclear en el terminal la orden. Por ejemplo, si quisiéramos almacenar la media de la distancia de frenado, podríamos teclear lo siguiente media.frenado<-mean(cars$dist) Como ya sabemos, mean(cars$dist) le dice a R que obtenga la media de la variable dist del data.frame cars. La combinación de caracteres < (menor que) y - (guión) indica que el resultado de la función debe asignarse a...1 Finalmente media.frenado es el nombre que hemos querido dar en este caso al objeto que almacenará la media. Si hacemos ls() veremos que, efectivamente, el nuevo objeto se ha incorporado al espacio de trabajo. A partir de ahora, para conocer el valor de ese estadistico deberemos teclear, símplemente, media.frenado y, lo que es más importante, podré manipular el valor de la media en operaciones algebraicas. Por ejemplo, si desease dividir esa media entre tres, teclearía media.frenado/3. El proceso sería el siguiente media.frenado<-mean(cars$dist) media.frenado [1] 42.98 media.frenado/3 [1] 14.32667 Como puedes imaginar, el último valor no se almacenará en el espacio de trabajo puesto que no ha sido asignado a ningún objeto. Sin embargo, si consideras que sería conveniente, ya sabes cómo puedes hacerlo muy rápidamente y casi sin teclear nada. Sólo debes recuperar con la tecla flecha-arriba la línea en la que introdujiste la orden de cálculo (en este caso la última), desplazarte en ella con las flechas-derecha-izquierda hasta el comienzo de la línea y teclear el nombre que quieras dar al objeto seguido del operador de asignación <-. La asignación es, como ya hemos dicho, una operación básica en R. En una sesión de trabajo pueden realizarse multitud de asignaciones. Por ese motivo es muy recomendable que elijamos nombres adecuados para los objetos. Es muy conveniente que el nombre nos recuerde inmediatamente qué es lo que almacena el objeto, pero tampoco debe ser excesivamente largo. La experiencia nos irá ayudando en la elección de nombres. Cuando hacemos ls() y vemos que hay objetos que hemos almacenado, pero que no nos interesan, podemos quitarlos del espacio de trabajo. Para eso está la función rm(). Así, rm(media.frenado) haría desaparecer el objeto media.frenado. Si queremos quitar varios objetos, no tenemos más que teclear sus nombres dentro de la función separados 1 recuérdese que puede sustituirse por el signo = en las versiones 1.5 y posteriores 3.4. PRIMERA SESIÓN 19 por comas. Así, rm(media.frenado,cars) quitaría esos dos objetos que, por el momento, deben ser todo lo que tenemos en el espacio de trabajo. Podemos comprobarlo con ls(): ls() [1] “cars” “media.frenado” rm(cars,media.frenado) ls() character(0) Ese character(0) nos dice que el espacio de trabajo está vacío. De momento, volveremos a cargar el archivo cars. Aunque estemos en los primeros pasos, seguramente también nos interesará analizar la distribución de frecuencias de las variables. Para generar un histograma de la variable speed deberemos teclear hist(cars$speed) lo que viene a traducirse por “coge el data.frame cars, selecciona la variable speed y haz el histograma con las opciones que tengas por defecto”. El resultado es el que vemos en la Figura 3.2. 2 . Vemos que al pulsar la tecla Intro se abre una pantalla nueva que R llama Device 2 y dice que está activa. Si quiero ver el histograma, pero ahora de la variable dist, tengo dos opciones: presentarlo en la pantalla que acaba de abrirse (en cuyo caso desaparecerá el histograma anterior), o abrir una nueva pantalla. Para ello deberemos teclear, en Linux, X11() y, en Windows, windows(). En ese momento se abrirá y activará automáticamente un nuevo dispositivo. Cualquier función gráfica que introduzcamos en R se representará en esa nueva pantalla. Abrir dispositivos de esta manera es una de las posibilidades que ofrece R para ver varios gráficos de forma simultanea. Podemos conocer en cualquier momento cuántos dispositivos tenemos abiertos tecleando dev.list(). Nos aparecerá por pantalla una lista que nos informa del tipo de dispositivo, y el número que tiene asignado. El último dispositivo abierto será siempre el que esté en estado activo a no ser que activemos otro. Esto podemos hacerlo con la función dev.set(). Tecleando dev.set(which=3) activaremos inmediatamente el dispositivo número 3. Si queremos cerrar un dispositivo, podemos hacerlo con el ratón, pulsando sobre la X de la esquina superior derecha. Sin embargo, hay dispositivos, como el postscript (que veremos más adelante), que no presenta ventana alguna. Para cerrarlo en ese caso debemos teclear, por ejemplo, dev.off(4) siendo en este caso 4 el número del dispositivo a cerrar. En muchos casos también vamos a interesarnos por la relación existente entre las distintas variables. Una aproximación gráfica la obtendremos mediante la función plot(). Para ver el gráfico dispersión de speed frente a dist deberemos teclear plot(cars$speed,cars$dist) lo que genera la Figura 3.3 2 Posteriormente estudiaremos más en profundidad los métodos gráficos y veremos cómo modificar y personalizar el gráfico resultante CAPÍTULO 3. PRIMEROS PASOS 20 dist 0 20 40 60 80 100 120 Figura 3.2: Hacer histogramas con hist() 5 10 15 20 25 speed Figura 3.3: Diagrama de dispersión con la función plot 3.5. ACABAR UNA SESIÓN DE TRABAJO 21 3.5. Acabar una sesión de trabajo Podemos dar por terminada nuestra primera sesión de R. Para salir sólo hay que teclear q(). En ese momento nos preguntarán si deseamos guardar los objetos presentes en el espacio de trabajo que han sido generados en la última sesión. Si pulsamos la tecla y, se guardarán, y nos los encontraremos de nuevo cuando arranquemos el programa. Si pulsamos n todos los objetos que no hubieran sido guardados en alguna sesión anterior de R se perderán sin posibilidad de recuperarlos. 22 CAPÍTULO 3. PRIMEROS PASOS Capítulo 4 Gestión de objetos y ficheros Después de conocer las bases de R, estamos en condiciones de comenzar a trabajar con datos propios y presentar al mundo los resultados de tus análisis. En este capítulo veremos cómo introducir datos en R, cómo manipularlos y cómo exportar los resultados para poder incorporarlos a un informe. 4.1. Introducción de datos en R Al llegar a este punto, podemos encontrarnos con varias situaciones: 1. en algunos casos, los datos los tendremos ya metidos en algún fichero (de hoja de cálculo, de base de datos,...) bien porque los introdujimos en un momento anterior, o porque nos los proporcionaron en esos formatos 2. podemos tener datos propios todavía sin digitalizar, y es posible que, después de conocer las posibilidades que te ofrece R, prefieras introducir los datos en una aplicación que te permita editarlos de una manera más ágil 3. finalmente, es posible que deseas introducir los datos directamente en R En primer lugar vamos a ver qué hay que hacer en los dos primeros casos para importar datos externos. 4.1.1. Importación de tablas externas: read.table() Por lo general, los datos los tenemos en forma de tablas en las que encontramos una serie de variables de diferentes tipos dispuestas en columnas; y un número variable de registros que aparecen ocupando las filas de la tabla. Sea cual sea la aplicación en la que tengamos la información, siempre podremos exportar la tabla en forma de fichero de texto. Esta es una de las formas más fáciles de trasladar datos desde cualquier aplicación hacia R. Tal vez lo más frecuente sea exportar las tablas a ficheros de texto en los que cada campo (columna, variable) quede 23 CAPÍTULO 4. GESTIÓN DE OBJETOS Y FICHEROS 24 limitado por tabuladores (tab delimited en inglés). Si tenemos una tabla como la siguiente: ALTITUD 500 1500 25 ORIENTACIÓN N TEMPERATURA 15 6 S y la exportamos (desde la aplicación en la que la hemos creado) con el nombre datos.txt al directorio de trabajo de R, podremos incorporarla al espacio de trabajo con la siguiente orden: datos<-read.table("datos.txt", header=T, sep="\t") en ella se dice que el fichero (que debe ir entrecomillado) tiene una primera fila en la que van los nombres de las variables (header=T) y que el separador de columnas es el tabulador (sep="\t" ). La orden podría funcionar correctamente aun sin colocar esa última opción siempre y cuando no haya ningún valor perdido (celdas en blanco) en la tabla original, pero como en nuestro caso sí los hay, es preciso introducirla. Si comprobamos el resultado, obtenemos lo siguiente datos ALTITUD 1 500 2 1500 3 25 ORIENTACIÓN N S TEMPERATURA 15 6 NA vemos que el valor perdido en la variable TEMPERATURA se ha convertido automáticamente en un NA (Not Avalaible en inglés), pero el perdido en la variable ORIENTACIÓN aparece en blanco. Esto se debe a que R transforma en NA’s sólo las varibles numéricas. Si queremos que los valores perdidos de una variable no numérica se transformen en NA’s para ser tratados como tales en R, debemos indicar a la hora de importar la tabla qué forma adoptan los valores perdidos en esos casos. Como en el nuestro (y es lo habitual) son celdas que están en blanco, lo que haremos será introducir la opción na.strings="" datos<-read.table("datos.txt", header=T, na.strings=“”, sep="\t") datos ALTITUD 1 500 2 1500 3 25 ORIENTACIÓN N NA S TEMPERATURA 15 6 NA Con esta orden de importación, las variables numéricas contenidas en la tabla de origen serán mantenidas como numéricas, pero las variables de tipo carácter serán transformadas en lo que en R se llama factores. Cuando conozcamos mejor cómo 4.1. INTRODUCCIÓN DE DATOS EN R 25 funciona R podremos decidir si eso nos conviene o no. Cuando deseemos mantener las variables de tipo carácter como tales, deberemos añadir a la función read.table la opción as.is=T. 4.1.2. Edición de datos en R En alguna ocasión es posible que deseemos introducir los datos directamente en R. Hay dos posibilidades para hacerlo: una es por la línea de comandos, y la otra mediante una pantalla gráfica. En la línea de comandos podemos crear un objeto y asignarle al mismo tiempo los valores que ha de almacenar. Por ejemplo, si quisiéramos introducir directamente los datos de la tabla anterior podríamos teclear lo siguiente: ALTITUD<-c(500,1500,25) ORIENTACIÓN<-c("N",NA,"S") TEMPERATURA<-c(15,6,NA) tabla<-data.frame(ALTITUD,ORIENTACIÓN,TEMPERATURA) En las tres primeras líneas creamos las variables y les asignamos los valores que adoptan. La función c() lo que hace es concatenar los tres valores para crear un vector. Los valores perdidos los introducimos como NA’s, y los valores de las variables no numéricas deben ir entrecomillados. En la cuarta línea hemos procedido a crear un data.frame que hemos llamado tabla en el que hemos reunido las tres variables. También podríamos haber realizado la operación en un sólo paso. Símplemente deberíamos haber tecleado: tabla<-data.frame(ALTITUD=c(500,1500,25),ORIENTACIÓN= c( "N",NA,"S"),TEMPERATURA=c(15,6,NA)) Si tecleamos str(tabla) obtenemos: str(tabla) ‘data.frame’: 3 obs. of 3 variables: $ALTITUD : num 500 1500 25 $ORIENTACIÓN: Factor w/ 2 levels "N","S": 1 NA 2 $TEMPERATURA: num 15 6 NA El mensaje nos indica que, efectivamente, hemos generado un data.frame que tiene tres observaciones y tres variables, dos de las cuales son numéricas y una tercera, la orientación, es lo que en R se llama un factor; en este caso, el factor presenta sólo dos modalidades: N y S. También nos advierte de la presencia de valores perdidos. Este sistema desde luego no es muy práctico si tenemos que introducir muchos datos. Una alternativa es utilizar un editor tipo hoja de cálculo al que podemos acceder con la función data.entry(). Esta posibilidad sólo está disponible en determinadas plataformas y GUI’s, por lo que no siempre funciona. Cuando funciona, disponemos de una herramienta muy útil. La única precaución que debemos guardar es la de crear previamente el objeto, por ejemplo un data.frame, antes de introducir los datos en él desde el editor. La secuencia de instrucciones en Windows podría ser la siguiente datos<-data.frame() Edit/Data editor... Con la primera instrucción creamos un data.frame vacío llamado datos. Luego vamos al menú Edit/Data editor.... Nos saldrá una ventana en la que nos pedirán el nombre del objeto a editar. Deberemos introducir el nombre, en este caso datos. Tras esta ope- 26 CAPÍTULO 4. GESTIÓN DE OBJETOS Y FICHEROS ración, se abre una rejilla de filas y columnas en las que podremos introducir nombres de variables (haciendo clic con el botón izquierdo del ratón en la cabecera de columna) y los valores que correspondan. Tras concluir la edición, cerramos la ventana haciendo clic sobre el aspa de la esquina superior derecha. Desde ese momento, podemos ver el contenido del data.frame tecleando datos. 4.2. Salida de información de R Como es lógico, parte de la información que obtengamos con R a lo largo de nuestro análisis (valores de estadísticos, gráficos,...) querremos llevarla fuera para incorporarla a alguna aplicación que nos permita editarla (por lo general un procesador de texto). En este apartado veremos algunas funciones que nos van a permitir llevar a cabo estas operaciones. 4.2.1. Exportación de gráficos En un apartado anterior hemos visto cómo generar un gráfico con la función plot(). Si quisiéramos exportarlo, podríamos hacerlo con la función dev2bitmap(), que transforma el contenido de la pantalla X11 activa en una imagen en formato bitmap. Lo único que habría que hacer es decidir con qué nombre quiero guardar el gráfico y teclear dev2bitmap("GráficoCars", res=300) con esta función guardamos el gráfico con el nombre de GráficoCars y con una resolución (res de 300 puntos por pulgada. Por supuesto, la resolución puede modificarse. El fichero gráfico se guarda en el directorio de trabajo, a no ser que en el nombre (que debe ir entrecomillado) se indique lo contrario. Por ejemplo, dev2bitmap(”/home/pablo/GráficoCars” , res=150), guardaría el gráfico (siempre que tengamos permiso de escritura) en el directorio /home/pablo. En este caso, lo hará con una resolución de 150 ppp. En Linux es muy habitual manejar ficheros gráficos en formato PS (Postscript). Para exportar el gráfico anterior en este formato habría que introducir la orden dev.print(postscript) lo que genera un fichero llamado Rplots.ps en la carpeta/directorio de trabajo. Con un visor PS podremos comprobar que el gráfico es realmente el que nos interesa. Posteriormente deberemos cambiar inmediatamente el nombre del fichero por uno que nos recuerde qué gráfico contiene, porque la próxima vez que ejecutemos dev.print(postscript) se generará un nuevo fichero Rplots.ps que borrará el contenido del anterior. Como puede apreciarse, este método resulta un poco lioso y arriesgado, de modo que lo habitual será exportar el gráfico con un nombre ya definido. Esto puede hacerse con la opción file. Así, si queremos guardar el fichero postscript con el nombre Grafico1, podríamos teclear dev.print(postscript, file="Grafico1") En la versión de Windows podemos acceder al menú para guardar el gráfico sólo con hacer clic con el botón derecho del ratón sobre la ventana gráfica. Entonces nos aparecen una serie de opciones y formatos para elegir. 4.3. MANIPULACIÓN DE DATA.FRAMES 27 4.2.2. Exportación de textos En muchos casos querremos exportar la información obtenida mediante análisis estadísticos y que R nos presenta por pantalla. Por ejemplo, el resultado de la función summary(), o de un análisis de regresión entre dos variables. Para ello disponemos de la función sink(). Esta función lo que hace es dirigir la salida de una orden introducida en R a un fichero de texto cuyo nombre debemos incluir en la primera llamada a la función. Tras introducir sink(), todo lo que hagamos en R (salvo gráficos) se almacenará en el fichero externo sin llegar a presentarse por pantalla. Obviamente, hay que decirle a R cuándo queremos dejar de extaer información. Eso lo haremos con un nueva llamada a sink(). A continuación se presenta un ejemplo de utilización de esta función sink(“resultados") str(cars) summary(cars) cor(cars) sink() Si exploramos el directorio de trabajo veremos que existe un nuevo fichero, de nombre resultados, que es el nombre que hemos introducido entrecomillado al llamar a la función. Si lo abrimos con un editor de texto confirmaremos que contiene la información esperada, en este caso, acerca de la estructura, estadísticas básicas y matriz de correlaciones del data.frame cars. Como hemos cerrado la exportación de resultados son sink(), los resultados de las operaciones que hagamos en adelante y que tengan salida, se presentarán en la pantalla. Es muy importante no olvidarse de cerrar la exportación. De lo contrario iremos llenando el fichero de resultados indeseados. Cuando queramos exportar un data.frame contenido en nuestro espacio de trabajo, podremos hacerlo con la función write.table(). La siguiente orden exportaría el data.frame cars al fichero coches write.table(cars, “coches") 4.3. Manipulación de data.frames Como hemos comentado anteriormente, los data.frame son un tipo de objeto fundamental en R. La mayor parte de los datos que queramos analizar estadísticamente estarán almacenados en forma de data.frame ya que, por lo general, tendremos una serie de variables numéricas y otras que no lo serán. Nada impide que en un data.frame todas las variables sean de un solo tipo. Cuando todas son numéricas, el data.frame puede transformarse en una matriz, que es otro tipo de objetos presentes en R. Se entiende, por tanto, que sea imprescindible conocer las técnicas de manipulación básicas de los data.frame. En unas ocasiones querremos extraer información de ellos; en otras, eliminar o añadir variables; seleccionar datos;. . . En los siguientes apartados veremos cómo hacer algunas de estas operaciones. Para los ejemplos de esta sección utilizaremos los datos contenidos en el fichero resultados académicos que se adjunta con el CD. Para cargar esos datos, es recomendable copiar el fichero en nuestro directorio de trabajo. Entonces, sólo con teclear 28 CAPÍTULO 4. GESTIÓN DE OBJETOS Y FICHEROS resultados<-read.table(resultados.txt",sep=",",header=T) tendremos en nuestro workspace el data.frame resultados. Como se puede apreciar, en la instrucción le indicamos a read.table que el fichero es de tipo delimitado por comas (sep="," ) y que la primera fila contiene el nombre de las variables(header=T). Como este es el data.frame que vamos a utilizar más a menudo, es una buena idea colocarlo en la ruta de búsqueda con attach(resultados), de esa manera podremos referirnos a las variables sin necesidad de indicar a R continuamente que se encuentran en el data.frame resultados. Una vez cargados los datos es conveniente estudiar su estructura con str() str(resultados) ‘data.frame’: 4838 obs. of 9 variables: $ Evaluacion: Factor w/ 4 levels "1","2","F","N": 4 3 3 2 2 3 3 3 2 3 ... $ Curso : num NA 1 2 1 1 1 1 2 2 1 ... $ B : num NA 0 0 0 0 0 0 0 0 0 ... $ IN : num NA 0 0 0 0 0 0 0 0 0 ... $ NT : num NA 0 0 0 1 1 1 1 1 1 ... $ SB : num NA 9 9 9 8 8 8 8 8 8 ... $ SF : num NA 0 0 0 0 0 0 0 0 0 ... $ Año : Factor w/ 9 levels "1994","1995",..: 9 5 6 8 5 7 7 5 6 5 ... $ Alumno : Factor w/ 4838 levels "1","10","100",..: 4838 4265 374 1973 2200 2431 2593 2598 3310 3311 ... 4.3.1. Selección de variables Ya sabemos cómo aplicar una función a una variable de un data.frame. Por ejemplo, length(resultados$NT) nos proporcionará el número de valores (incluidos los perdidos) que tiene la variable NT (número de Notables). Sin embargo, hay otras maneras equivalentes de obtener el mismo resultado. Entre otras, las siguientes length(resultados[,6]) length(resultados[[6]]) length(NT) como puede verse, en los dos primeros casos hemos sustituido el nombre de la variable (NT) por el número de orden que ocupa en el data.frame (el notable es la variable número 6). En el tercer caso hemos obviado la referencia al nombre del data.frame; esto sólo funcionará siempre y cuando antes lo hayamos colocado en la ruta de búsqueda con attach(). 4.3.2. Extracción de variables En ocasiones el fichero de datos original tiene más variables de las que nos interesan para nuestro análisis. En esos casos, puede interesarnos crear un nuevo data.frame en el que sólo aparezcan las variables que realmente vayamos a necesitar. Esta operación puede realizarse con la función subset(). Esta es una función muy útil y la estudiaremos también en otra sección. 4.3. MANIPULACIÓN DE DATA.FRAMES 29 Para extraer variables hay que indicar a la función el nombre del data.frame y las variables que deseamos extraer. Como queremos generar un data.frame nuevo, deberemos realizar una operación de asignación. Por ejemplo, para crear un data.frame llamado fracasoescolar con las variables Año, Evaluacion, Curso e IN deberíamos teclear fracasoescolar<-subset(resultados,select=c(8,1,2,4)). Si solicitamos a R la estructura del nuevo data.frame con str(fracasoescolar) veremos lo siguiente: ‘data.frame’: 4838 obs. of 4 variables: $ Año : Factor w/ 8 levels "1994","1995",..: 5 6 8 5 7 7 5 6 5 6 ... $ Evaluacion: Factor w/ 3 levels "1","2","F": 3 3 2 2 3 3 3 2 3 1 ... $ Curso : num 1 2 1 1 1 1 2 2 1 2 ... $ IN : num 0 0 0 0 0 0 0 0 0 0 ... Como se ve, han desaparecido las variables no deseadas y se han seleccionado las que queremos, respetando el orden en el que se han introducido en la función subset(). Vamos a estudiar un poco en detalle cómo se indica qué variables se quieren extraer. Esto de hace con el argumento select. En el ejemplo hemos tecleado select=c(8,1,2,4). Ese c(8,1,2,4) indica a R que concatene las variables que ocupan el octavo, primero, segundo y cuarto lugar. La c que precede al paréntesis es imprescindible y se utiliza en R siempre que se quiere crear lo que se llama un vector. En lugar de número de orden, podríamos haber utilizado el nombre de las variables, así fracasoescolar<-subset(resultados,select=c(Año,Evaluacion,Curso,IN) habría producido el mismo resultado. En ocasiones son más las variables que quiero extraer que las que deseo descartar. En esos casos es más conveniente utilizar otra forma de selección. Por ejemplo fracasoescolar<-subset(resultados,select=-c(B,NT,SB) incluiría en el data.frame fracasoescolar todas las notas menos (como le indica el signo - delante del operador de concatenación) el B, el NT y el SB. Si, por casualidad, las variables a extraer son consecutivas, podríamos seleccionarlas en bloque. Así, probatinas<-subset(resultados,select=(1:5)) crearía un data.frame en el que se incluirían las primeras cinco variables. Nótese que, en este caso, no se introduce la concatenación; R entiende los dos puntos entre el 1 y el 5 como desde la uno a la cinco, ambas inclusive. 4.3.3. Selección de casos Al igual que hemos seleccionado variables para aplicarles procedimientos estadísticos, también podemos seleccionar casos para aplicarles funciones. Por lo general, esta selección se basará en el cumplimiento de determinadas condiciones. Los operadores habituales en las expresiones condicionales son 1. ==, igual a 2. >=, mayor o igual a 3. <=, menor o igual a 30 CAPÍTULO 4. GESTIÓN DE OBJETOS Y FICHEROS 4. !=, diferente a 5. &, y 6. |, o En nuestro data.frame sobre resultados académicos se almacenan datos correspondientes a varios cursos escolares. En alguna ocasión, seguro que querremos conocer estadísticas relativas a un año en concreto. Si quisiéramos conocer la mediana del número de insuficientes en el año 1999, podríamos introducir la siguiente orden median(IN[Año==1999]) la condición de selección se indica entre corchetes. En este caso se dice: coge los registros correspondientes a 1999, fíjate en la variable IN y calcula su mediana1. Las condiciones de selección se pueden anidar. Por ejemplo, si queremos analizar sólo la evaluación final, escribiríamos median(IN[Año==1999&Evaluación=="F"]) En muchas ocasiones nos interesará (o, incluso, será preciso) eliminar los valores perdidos para efectuar un análisis. Por ejemplo, la función median() no funciona si existen NA’s en la variable. En muchas ocasiones podemos remediar el problema introduciendo la opción na.rm=T en la función como vemos a continuación. median(IN) [1] NA median(IN,na.rm=T) [1] 2 En el primer caso la función no actúa correctamente; en el segundo, sí. 4.3.4. Extracción de casos Esta es otra de las operaciones básicas del análisis estadístico, especialmente cuando tenemos ficheros de datos muy grandes. Como casi todo en R, hay varias maneras de llevar a cabo tal selección. Una de ellas es mediante la función subset(). En este caso introduciremos una condición que afecta a los registros y no a las variables. Por ejemplo, para seleccionar alumnos/as con más de 3 insuficientes deberíamos proceder de la siguiente manera granfracasoescolar<-subset(resultados,IN>3) en este caso, a continuación del nombre del data.frame se introduce el nombre de la variable, y la condición de selección. Hay que hacer notar que la condición de igualdad se indica con doble signo igual (==). Para ver el resultado, utilizaremos de nuevo str() str(granfracasoescolar) ‘data.frame’: 1581 obs. of 9 variables: $ Evaluacion: Factor w/ 3 levels "1","2","F": 2 1 2 3 1 2 3 3 3 2 ... $ Curso : num 2 1 1 2 1 2 1 2 1 1 ... $ B : num 0 0 0 0 0 0 0 0 0 0 ... $ IN : num 0 0 0 0 0 0 0 0 0 0 ... $ NT : num 3 3 3 3 3 3 3 3 3 3 ... 1 Como hemos hecho previamente attach(resultados), podemos referirnos directamente a IN. De lo contrario, deberíamos haber tecleado resultados$IN 4.3. MANIPULACIÓN DE DATA.FRAMES 31 $ SB : num 6 6 6 6 6 6 6 6 6 6 ... $ SF : num 0 0 0 0 0 0 0 0 0 0 ... $ Año : Factor w/ 8 levels "1994","1995",..: 5 5 8 7 5 8 5 7 6 7 ... $ Alumno : Factor w/ 4838 levels "1","2","3","4",..: 3117 3208 2692 3834 3619 3896 4024 4188 2693 3835 ... Podemos ver que el nuevo data.frame sigue teniendo las mismas 9 variables pero el número de casos se ha reducido de 4838 a 1581. En muchas ocasiones la condición de selección es múltiple. En ese caso no hay más que encadenarlas con el signo &. Por ejemplo, para seleccionar el alumnado con más de tres notables y con tres bienes habría que teclear selección<-subset(resultados,NT>3 & B==3). Una manera menos intuitiva de conseguir el mismo resultado es la siguiente selección<-resultados[resultados$NT>3& resultados$B==3 ] Combinando las dos posibilidades estudiadas anteriormente, se puede realizar una extracción simultanea de variables y casos. Así, para crear un data.frame que contenga sólo el número de caso y el número de insuficientes del alumnado correspondiente al año 1998 podríamos teclear otraseleccion<-subset(resultados,Año==1998,select=c(Alumno,IN)) En alguna ocasión puede interesarnos eliminar del análisis todos los datos en los que haya algún valor perdido. Como veremos, esta puede ser una medida extrema ya que hay funciones en R que nos permiten ignorar los casos con valores perdidos para las variables implicadas sin necesidad de eliminarlas del data.frame (ver la sección de Estadística básica). En cualquier caso, si quisiéramos hacerlo, podríamos teclear sinNA<-na.omit(resultados) lo que crearía un nuevo data.frame en el que encontraremos sólo los casos completos. 4.3.5. Introducción de variables Hay dos situaciones que pueden hacer necesaria la entrada de nuevas variables en un data.frame. 1. cuando deseamos obtener una nueva variable calculada a partir de variables ya contenidas en el data.frame 2. cuando necesitamos actualizar los datos que teníamos El primer problema es de naturaleza matemática; el segundo es un problema más bien de edición. Veamos cómo podemos afrontar ambas situaciones. Añadir variables calculadas a un data.frame Resulta bastante sencillo llevar a cabo esta operación. Supongamos que quisiéramos introducir en nuestro data.frame resultados un índice de resultados académicos del estilo indice = (NT + SB/IN + SF + B + NT + SB) ∗ 100 Para llevarlo a cabo deberíamos teclear la siguiente orden resultados$indice<-(NT+SB)/(IN+SF+B+NT+SB)*100 32 CAPÍTULO 4. GESTIÓN DE OBJETOS Y FICHEROS que viene a decir calcula la suma de notables y sobresalientes, divídela entre el número total de notas, multiplica el resultado por cien y colócalo en la variable que llamo indice en el data.frame resultados. (Nótese que, de nuevo, presuponemos que el data.frame resultados está en la ruta de búsqueda. De lo contario, no podríamos llamar directamente a las variables como hemos hecho en la orden anterior). Como vemos, el problema se reduce a una operación de asignación; cuestión que ya hemos analizado anteriormente. El resultado sería el siguiente str(resultados) ‘data.frame’: 4838 obs. of 10 variables: $ Evaluacion: Factor w/ 3 levels "1","2","F": 3 3 2 2 3 3 3 2 3 1 ... $ Curso : num 1 2 1 1 1 1 2 2 1 2 ... $ B : num 0 0 0 0 0 0 0 0 0 0 ... $ IN : num 0 0 0 0 0 0 0 0 0 0 ... $ NT : num 0 0 0 1 1 1 1 1 1 1 ... $ SB : num 9 9 9 8 8 8 8 8 8 8 ... $ SF : num 0 0 0 0 0 0 0 0 0 0 ... $ Año : Factor w/ 8 levels "1994","1995",..: 5 6 8 5 7 7 5 6 5 6 ... $ Alumno : Factor w/ 4838 levels "1","2","3","4",..: 4838 1334 2774 2979 3186 3331 3336 3978 3979 2607 ... $ indice : num 100 100 100 100 100 100 100 100 100 100 ... como podemos observar, en el data.frame resultados aparece en último lugar la nueva variable indice. Incorporación de nuevas variables Puede ocurrir que en un momento determinado de nuestra investigación necesitemos añadir alguna variable a nuestros registros. Como hemos dicho antes, esto es más bien un problema de edición y, por lo general, resulta mejor idea resolverlo fuera de R e incorporar el nuevo conjunto de datos por los medios de importación que ya hemos estudiado anteriormente. Sin embargo, vamos a ver algunas maneras de resolver la cuestión dentro de R. Si tenemos dos data.frames con uno o varios campos (utilizando la terminología de las bases de datos) en común, podemos utilizar la función merge() para incorporar campos (variables) de uno a otro. Como siempre que se desea enlazar dos tablas, debemos indicar sus nombres, y qué campos tienen en común. Supongamos que tenemos dos data.frame como los siguientes enfe Nombre Enero Febrero 1 María 10 20 2 José 20 10 3 Sara 30 40 ma Nombre Marzo 1 María 13 2 José 45 3 Sara 32 4.3. MANIPULACIÓN DE DATA.FRAMES 33 enfe almacena los puntos acumulados por tres alumnos/as en los meses de enero y febrero. ma almacena los que han almacenado, los mismos alumnos durante marzo. Para añadir los datos de marzo a los de enero y febrero deberíamos teclear merge(enfe,ma,by.x="Nombre",by.y="Nombre") el resultado sería Nombre Enero Febrero Marzo 1 José 20 10 45 2 María 10 20 13 3 Sara 30 40 32 Como se ve, el resultado es el deseado. En este caso nos ha parecido conveniente crear un nuevo data.frame (mediante una asignación) con los datos de los tres meses. Si quisiéramos añadir la variable a un data.frame existente sin crear uno nuevo, lo único que deberíamos hacer es asignar el resultado de la función merge a dicho data.frame. En este caso, enfe<- merge(enfe,ma,by.x="Nombre",by.y="Nombre") añadiría los datos de marzo al data.frame enfe. En muchas ocasiones, los data.frames a unir no tienen exactamente los mismos registros. En el caso anterior, por ejemplo, en el mes de marzo podría haberse incorporado algún alumno nuevo. Para que le resultado de la unión recoja todos los registros contenidos en uno y otro data.frame debemos modificar un poco la orden merge(enfe,ma,by.x="Nombre",by.y="Nombre",all.x=T,all.y=T) con esos all.x=T y all.y=T (la T hace referencia al inglés TRUE, VERDAD) le decimos que incorpore todos los casos (all) del data.frame x (el que ponemos en primer lugar), y también del data.frame y . Como es natural, en este caso se completarán con NA’s los valores de las variables para los que no tengamos datos. Hay que tener en cuenta que sin esos dos argumentos, sólo los casos comunes a ambos data.frame serían seleccionados. Finalmente, si sólo quisiéramos considerar los casos incluidos en uno de los data.frame, deberíamos indicarlo en la orden. Por ejemplo, merge(enfe,ma,by.x="Nombre",by.y="Nombre",all.y=T) daría como resultado la unión considerando sólo los alumnos presentes en el mes de marzo, que es el data.frame y. 4.3.6. Incorporación de registros Vamos a considerar ahora la posibilidad de añadir a un data.frame nuevos registros de las mismas variables. En el apartado anterior hemos visto que la función merge() permitía incorporar casos nuevos cuando añadíamos variables a un data.frame. Sin embargo, en ese caso, los nuevos registros no presentaban valores para las variables previas. En el caso de que sí lo hicieran, los valores de los registros nuevos no se incorporarían a la variable del data.frame previo sino que la variable se duplicaría. Por lo tanto, para añadir registros a un data.frame utilizaremos otra función: rbind(). Supongamos que al dataframe enfe con el que trabajábamos antes queremos añadirle los casos contenidos en este otro llamado nuevo Nombre Enero Febrero Marzo 34 CAPÍTULO 4. GESTIÓN DE OBJETOS Y FICHEROS 1 Manuel 45 1 14 2 Aurora 10 2 32 3 Jorge 2 4 20 Si ejecutamos union<-rbind(enfe,nuevo), conseguiremos nuestro cometido. El resultado será: Nombre Enero Febrero Marzo 1 José 20 10 45 2 María 10 20 13 3 Sara 30 40 32 4 Manuel 45 1 14 5 Aurora 10 2 32 6 Jorge 2 4 20 Hay que hacer notar que no es preciso que el orden de las variables en los dos data.frame sea el mismo, pero sí el nombre de las mismas. Por otro lado, no hay problema en unir data.frames en los que las variables sean de tipo numérico o de carácter, pero si alguna de ellas es un factor, de todas las clases contenidas en el data.frame que vamos a añadir, sólo las que estén presentes en el data.frame de destino serán reconocidas. Todas las demás serán ignoradas y transformadas en NA’s con la consiguiente pérdida de información. Por lo tanto, si nuestros data.frames contienen factores, puede ser conveniente transformarlos en vectores (con la función as.vector()) antes de proceder a incorporar datos de uno a otro. Otra posibilidad sería almacenar las variables de tipo carácter como tales (sin transformarlas en factores) al leer ficheros externos. Para ello habría que añadir la opción as.is=T en la función read.table(). 4.3.7. Ordenación de variables En algunas ocasiones es necesario disponer los casos en orden ascendente del valor de una variable. En R podemos llevar a cabo esta ordenación con la función sort(). Así, sort(cars$speed) nos devolverá un vector con los valores de la variable de los casos ordenados de menor a mayor. La función order() también está relacionada con las operaciones de ordenación de variables. En este caso, la función nos devuelve un vector que nos indica en qué posición se encuentra el caso con el valor mínimo, el segundo más bajo, el tercero, . . . , hasta llegar a indicar la posición que ocupa el valor máximo. Obviamente, si la variable está en orden ascendente, order() proporcionará el vector (1,2,3,....,n) siendo n el número de casos. Capítulo 5 Estadística descriptiva En este capítulo veremos cómo realizar en R una serie de análisis estadísticos básicos. Se da por supuesto que el lector conoce el cometido y significado de todos ellos, por lo que las referencias en este sentido serán reducidas. 5.1. Análisis exploratorio de los datos En este apartado vamos a considerar algunas de las posibilidades que ofrece R para recabar información básica de la naturaleza de las variables que deseamos analizar. Además estudiaremos algunos procedimientos gráficos: el gráfico stem and leaf, el boxplot y el histograma. 5.1.1. Medidas características Como dijimos anteriormente, la función summary() proporciona información básica acerca de una variable. También acepta como argumento un data.frame, en cuyo caso la salida informará acerca de todas las variables contenidas en el mismo. Otras funciones nos dan medidas estadísticas de forma aislada. Ya comentamos algunas de ellas (mean(),median(),max(),min(),sum(), var()). Otras son range(), que nos muestra los valores mínimo y máximo, pero no el rango; sd(), que devuelve la desviación estandard; y quantile(), que nos va a permitir dividir la variable en partes iguales para obtener los cuartiles, deciles o percentiles. Veamos su funcionamiento para la variable IN quantile(resultados$IN,probs=1:10/10,na.rm=T) 10 % 20 % 30 % 40 % 50 % 60 % 70 % 80 % 90 % 100 % 0 0 0 1 2 3 4 5 7 10 Como vemos, con la función hemos obtenido los diez deciles de la variable. Sin embargo, no existe ninguna función que nos proporcione un listado o una tabla con todas las medidas estadísticas que puedan interesarnos. De lo que sí dispone R (por 35 36 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA ser en definitiva un lenguaje de programación) es la posibilidad de editar personalmente operaciones aritméticas y de crear funciones que realicen determinadas operaciones. En el siguiente ejemplo se puede ver cómo se crea una función que nos devuelva la media, suma, desviación estandard, variancia y rango de una variable. Una vez creada, podremos aplicarla a cualquier variable. estadisticos <-function(x){ + cat("\n","La media es",mean(x),"\n") + cat("\n","La suma es",sum(x),"\n") + cat("\n","La desviacion estandard es",sd(x),"\n") + cat("\n","La variancia es",var(x),"\n") + cat("\n","El rango es","[",range(x),"]","\n") + }1 Las funciones deben tener un nombre, en este caso estadisticos, y su cuerpo va encerrado entre llaves ({}) . En el ejemplo, la función es una sucesión de funciones cat() (que encadena secuencias decaracteres) y las funciones estadísticas que queremos obtener. Por supuesto, pueden añadir todas las que deseen. . . Una vez creada, se puede aplicar a cualquier variable; por ejemplo cars$speed. Así, estadisticos(cars$speed) devuelve La media es 15.4 La suma es 770 La desviacion estandard es 5.287644 La variancia es 27.95918 El rango es [ 4 25 ] Esta función estará disponible en el espacio de trabajo siempre y cuando sea guardada (por ejemplo, al abandonar R, comunicando que deseamos guardar las modificaciones) y no sea borrada del mismo (con rm(estadisticos). Si queremos que la función esté disponible en cualquier otro espacio de trabajo, podemos introducirla en el fichero de funciones de R. Otra posibilidad sería crear un paquete con nuestras propias funciones, pero esta posibilidad de momento no vamos a considerarla. Una de las ventajas de R es que es software libre, de modo que se distribuye con el código fuente en formato editable. De esta manera, podemos modificar el programa según nuestros intereses. En este caso, lo que nosotros vamos a hacer es introducir una función nueva, creada por nosotros mismos, en el paquete base de R. Para ello (en Linux) no tenemos más que abrir el fichero /usr/lib/R/library/base/R/base (la localización puede variar en función de la distribución de Linux) con cualquier editor de texto y teclear la función tal y como lo hemos hecho en R. Por supuesto, debemos tener permiso de escritura sobre el fichero, cuyo propietario será por defecto root. Al guardar los cambios, la nueva función estará disponible siempre que arranquemos R ya que el paquete base se carga automáticamente. Como hemos adelantado anteriormente, en R también podemos construir expresiones algebráicas que nos permiten calcular medidas no implementadas en los paquetes 1 El signo + que aparece en el texto no se introduce por teclado sino que es añadido automáticamente por R cuando en la linea de comandos pulsamos la tecla Intro para añadir un salto de linea 5.1. ANÁLISIS EXPLORATORIO DE LOS DATOS 37 con los que trabajemos. Por ejemplo, si necesitamos conocer la media geométrica de una variable, sabiendo que se define como G= √ n x1 · x2 . . . · xn podemos introducir (en el caso de cars$dist) prod(cars$dist)^(1/length(cars$dist)) donde prod() es la función que calcula el producto de los elementos de un vector y length() devuelve el número de elementos de un vector. Por supuesto, si vamos a requerir a menudo la media geométrica, podemos construir una función que acepte como argumento cualquier variable. En este caso podría ser media.geometrica<-function(x) {prod(x)^(1/length(x)) } Así, media.geometrica(cars$dist) devolvería [1] 34.32615 5.1.2. El gráfico stem and leaf Este gráfico constituye una manera rápida y efectiva de aproximarnos a la distribución de frecuencias de una variable. La función que debemos utilizar es stem(). Por ejemplo stem(cars$speed) da la siguiente salida por pantalla The decimal point is at the | 4| 6| 8| 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 00 00 00 00000 00000000 0000000 00000 0000000 00000 00 00000 Podemos expandir la escala (vemos que los intervalos en los que se ha dividido la variable van de dos en dos) introduciendo la opción scale en la función. A continuación vemos el resultado stem(cars$speed, scale=3) The decimal point is at the | 4| 5| 6| 00 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 38 7| 8| 9| 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 00 0 0 000 00 0000 0000 0000 000 00 000 0000 000 00000 0 0 0000 0 25 | 5.1.3. El gráfico boxplot Este es uno de los clásicos del análisis exploratorio. Permite hacernos una idea de la distribución de la variable a partir de los cuartiles y los valores extremos. Existen muchas posibilidades para personalizar y mejrar el resultado que genera la función boxplot() sin opciones, pero serán tratadas más en profundidad en el capítulo dedicado a los procedimientos gráficos. De momento veremos sólo su uso básico. Para obtener el boxplot de la variable dist del data.frame cars habría que teclear simplemente boxplot(cars$dist) obteniéndose la Figura 5.1. También se puede representar más de una variable en el mismo gráfico. Para ello no hay más que introducirlas como argumentos de la función. A continuación vemos cómo obtener los boxplot de las variables IN,SF,B,NT,SB del data.frame resultados boxplot(resultados$IN,resultados$SF,resultados$B,resultados$NT, resultados$SB) (recordar que podríamos evitarnos teclear continuamente la referencia a resultados si previamente hemos hecho attach(resultados) ) en la pantalla gráfica aparecerá la Figura 5.2. En ella pueden observarse todos los elementos típicos de los diagramas de caja, como los valores extremos y los valores atípicos. También queda patente que la figura merece un retoque profundo, pero eso aprenderemos a hacerlo posteriormente. Puede ser muy interesante que, en el boxplot, la anchura de las cajas sea variable en función del número de casos representado en cada categoría. Para ello hay que utilizar el parámetro width, que permite introducir un vector de anchuras. En nuestro caso la anchura dependerá del número de IN,SF,B,NT y SB presentes en el data.frame. Para conseguir la Figura 5.3 hemos hecho lo siguiente a<-c(sum(IN,na.rm=T),sum(SF,na.rm=T),sum(B,na.rm=T), 5.1. ANÁLISIS EXPLORATORIO DE LOS DATOS 0 20 40 60 80 100 120 39 0 2 4 6 8 10 Figura 5.1: Diagrama de caja con la función boxplot() 1 2 3 4 5 Figura 5.2: Varios diagramas de caja con la función boxplot() 40 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA sum(NT,na.rm=T),sum(SB,na.rm=T)) boxplot(IN,SF,B,NT,IN,width=a) Con la primera instrucción creamos el vector de anchuras, que es resulta de sumar el número de IN,SF,B . . . descontando los valores perdidos. En la segunda introducimos ese vector en el argumento width de la función. 5.1.4. El histograma El histograma es uno de los gráficos estadísticos más populares, y es uno de los habituales en el análisis exploratorio de las variables porque conocer la distribución de frecuencias de las mismas es una cuestión realmente importante. Ya hemos visto antes cómo usar la función hist() sin más argumento que la serie de datos a representar para obtener la Figura 3.2. Ahora vamos a ver cómo controlar algunas de las cuestiones básicas en el diseño (no tanto en la presentación, cosa que veremos en el capítulo final) del histograma. Una cuestión importante (más bien, importantísima) es la definición de los intervalos de las clases en que vamos a fragmentar la variable. Eso lo haremos con el argumento breaks. A continuación vemos cómo se utiliza con un ejemplo que da lugar a la Figura 5.4. hist(cars$speed,labels=T,breaks=0:9*3,col=3) hist(cars$speed,labels=T,breaks=0:9*4,col=3) hist(cars$speed,labels=T,breaks=0:14*2,col=3) Con la primera instrucción creamos el histograma de la izquierda. Como vemos, los límites de clase son 0, 3, 6, 9,. . . ,27. Para evitarnos poner todos esos valores, introducimos la expresión 0:9*3, que multiplica por 3 los enteros que van del 0 al 9 (ambos incluidos). Con la segunda obtenemos el histograma del medio. En este caso los intervalos tienen una amplitud de 4 unidades y el rango va desde 0 a 36. Finalmente, con la tercera instrucción obtenemos la figura de la derecha, en la que las clases van de dos en dos desde el 0 hasta el 28. En todos los casos, con el argumento col=3 hacemos que los histograms sean de color verde. Con labels=T hacemos que se impriman los valores de frecuencia asociados a cada clase. Como puede observarse, la definición de las clases determina la forma del histograma. Veremos más adelante cómo evitar este problema. Podemos hacer tanto histogramas de frecuencias absolutas como de frecuencias relativas. Por defecto, son del primer tipo. Para conseguir los segundos no hay más que incluir el argumento freq=F. Así, hist(cars$speed,labels=T,col=2,freq=F) genera la Figura 5.5. En ella se aprecia que el eje de ordenadas está etiquetado como density y no como frequency. 5.1.5. Contraste de normalidad Asegurarnos de que una variable se ajusta a la distribución normal es un requisito previo a la aplicación de muchos procedimientos estadísticos y por ello debe realizarse 5.1. ANÁLISIS EXPLORATORIO DE LOS DATOS 0 2 4 6 8 10 41 1 2 3 4 5 Figura 5.3: Diagramas de caja de anchura variable con el argumento width 15 10 9 6 6 10 4 Frequency 5 Frequency 10 8 6 6 5 4 3 5 6 0 1 0 5 10 15 cars$speed 20 25 1 1 0 5 10 15 20 25 cars$speed 0 0 30 35 0 0 2 2 1 0 2 2 3 2 0 Frequency 8 7 8 4 8 13 4 9 Histogram of cars$speed 8 Histogram of cars$speed 11 15 Histogram of cars$speed 0 0 5 0 10 15 cars$speed Figura 5.4: Modificación de las clases del histograma con breaks 20 25 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 42 0.07 Histogram of cars$speed 0.068 0.04 0.03 0.028 0.028 0.01 0.02 Density 0.05 0.06 0.068 0.00 0.008 0 5 10 15 20 25 cars$speed Figura 5.5: Frecuencias relativas con freq=F en las primeras fases de cualquier análisis estadístico. Vamos a considerar dos posibles aproximaciones al problema: el método gráfico y el test de Shapiro-Wilk . Método gráfico Existen varias posibilidades para llevar a cabo este análisis. La primera y más clásica es la representación del histograma de frecuencias de la variable (por ejemplo, Figura 5.6)2 . Sin embargo, el histograma presenta por lo general el problema de la elección del número y amplitud de las clases. Estos problemas se resuelven si, en lugar de representar el histogrma, representamos la función de densidad de la variable. Para ello, R dispone de la función density(). Así, tecleando plot(density(cars$speed)) se obtiene la (Figura 5.7). En este caso, permite apreciar un buen ajuste de los datos a la ley normal. Otro método gráfico es la observación del gráfico Q-Q (cuantil-cuantil) de la variable. En este gráfico se representan los valores observados en el eje de ordenadas y los valores teóricos en el de las abcisas. Cuando la variable sigue una ley normal, los puntos deben alinearse en línea recta. Para ilustrar el caso, vamos a considerar si determinado índice de resultados académicos (definido como una combinación lineal del número de IN,SF, B,NT y SB acumulados por el alumnado del data.frame resultados) se ajusta a la distribución normal. La 2 En el capítulo final se explica cómo añadir al histograma la curva normal 5.1. ANÁLISIS EXPLORATORIO DE LOS DATOS 43 Density 0.0 0.1 0.2 0.3 0.4 Histogram of scale(cars$speed) −2 −1 0 1 2 scale(cars$speed) Figura 5.6: Histograma con la función hist() Density 0.00 0.01 0.02 0.03 0.04 0.05 0.06 density(x = cars$speed) 0 5 10 15 20 25 30 N = 50 Bandwidth = 2.15 Figura 5.7: Función de densidad con plot(density()) 44 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA secuencia de instrucciones a introducir sería la siguiente resultados$indice<-(-2*IN)+SF+(2*B)+(3*NT)+(4*SB) qqnorm(resultados$indice) Con la primera instrucción creamos el índice y lo añadimos como una variable nueva al data.frame resultados. Con la segunda pedimos que dibuje el gráfico Q-Q de la nueva variable. El resultado es el de la Figura 5.8. En este caso se aprecia claramente que la variable no sigue la ley normal porque la nube de puntos no se ajusta en absoluto a una recta. Sin embargo, en muchas ocasiones la valoración visual puede llegar a ser problemática. Por ejemplo, si analizamos la misma variable seleccionando sólo los casos correspondientes al año 1996 (Figura 5.9), vemos que no es tan fácil decidir si se ajusta o no a la ley normal ya que parece haber un segmento lineal más definido. Para estos casos es conveniente aplicar alguna prueba de inferencia no paramétrica como el test de Shapiro-Wilk que estudiamos a continuación. Contraste de Shapiro-Wilk Una de las características que diferencia los métodos numéricos de los gráficos es que vamos a poder introducir un nivel de significación para aceptar o rechazar la hipótesis de ajuste a la distribución teórica. Para el caso concreto de la distribución normal, el test que vamos a estudiar es una de las mejores opciones que nos proporciona R. Se lleva a cabo con la función shapiro.test() y a continuación se aplica al caso anterior con un nivel de significación del 5 % shapiro.test(resultados$indice[Año==1996]) Shapiro-Wilk normality test data: resultados$indice[Año == 1996] W = 0.9805, p-value = 0.00249 Al ser la probabilidad p < 0,05, se debe rechazar la hipotesis de ajuste a la distribución normal con ese nivel de significación, eliminando la duda que podía introducir el análisis meramente gráfico. Sin embargo, aplicado a la variable cars$speed, shapiro.test(cars$speed) Shapiro-Wilk normality test data: cars$speed W = 0.9776, p-value = 0.4576 En este caso, la probabilidad asociada al estadístico w de Shapiro-Wilk (0,4576) es mayor que 0,05, por lo que se situa dentro de la zona de aceptación de la hipótesis de ajuste a la ley normal. Así, el test confirma lo que parecía adelantar la representación gráfica de la función de densidad (5.7). 5.1. ANÁLISIS EXPLORATORIO DE LOS DATOS 45 10 −20 −10 0 Sample Quantiles 20 30 40 Normal Q−Q Plot −4 −2 0 2 4 Theoretical Quantiles Figura 5.8: Gráfico Q-Q con la función qqnorm() 10 −20 −10 0 Sample Quantiles 20 30 40 Normal Q−Q Plot −3 −2 −1 0 1 2 3 Theoretical Quantiles Figura 5.9: Otro gráfico Q-Q con la función qqnorm CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 46 5.2. Cómo resumir la información contenida en un data.frame Desde sus inicios, la Estadística ha sido entendida como una disciplina cuyos fundamentos básicos eran el almacenamiento y análisis de grandes cantidades de datos. En este apartado vamos a estudiar cómo realizar en R lo que, aun hoy, la mayor parte de la gente considera como estadísticas, es decir, la síntesis en forma de tablas de recuentos, frecuencias,. . . de grandes bases de datos. Para ello son fundamentales las funciones table() y las diferentes variantes de la función apply(). 5.2.1. Tablas de frecuencias absolutas Estas son las tablas más básicas. Nos informan del número de casos que presentan cada una de las modalidades de determinada variable. Analizaremos por separado la creación de tablas de diferente dimensión. Tablas unidimensionales Estas son las más sencillas de todas. Sólo tomamos en consideración una variable. Por ejemplo, para hacer un recuento del número total de alumnos con 0, 1, 2, ..,10 insuficentes presentes en el data.frame resultados deberíamos teclear simplemente (siempre que resultados haya sido puesto en ruta de búsqueda con attach()) table(IN) cuyo resultado es table(IN) IN 0 1 2 3 4 5 6 7 8 9 10 1644 703 510 410 329 321 317 225 172 113 93 En muchos casos nos interesa agrupar los casos en intervalos. Para ello, podemos utilizar la función cut(). Por ejemplo, cut(IN,c(-0.01,2,5,8,10)) agrupará los registros de resultados en los intervalos definidos por los límites de clase −0,01, 2, 5, 8, 10 . Hay que hacer notar que cut(), por defecto, genera intervalos semiabiertos por abajo, de modo que para incluir en la primera clase los casos con 0 insuficientes, hay que definir un límite de clase ligeramente inferior (en este caso, −0,01). Ahora, para tabular el número de casos que contiene cada clase podríamos utilizar la función tapply() de la siguiente manera: tapply(Alumno,cut(IN,c(-0.01,2,5,8,10)),length) cuyo resultado es (-0.01,2] (2,5] (5,8] 2858 1061 715 (8,10] 207 Veamos con más detalle cuál es la sintaxis de la función aplicada: se le dice a R que coja la variable Alumno y reparta los casos en los intervalos definidos por la función 5.2. CÓMO RESUMIR LA INFORMACIÓN CONTENIDA EN UN DATA.FRAME 47 cut() según el número de IN que tenga cada uno. Una vez hecho este reparto, se aplica la función length() para ver cuál es la longitud del vector resultante, es decir, el número de casos. En este ejemplo no podemos utilizar la función sum() para obtener este último valor, como parecería más normal, porque la variable Alumno no es de tipo numérico sino que es un factor, de modo que no se le pueden aplicar operaciones de tipo algebráico. Como vemos, la tabla resultante nos informa de que hay 2858 alumnos/as con 0, 1 o 2 insuficientes, 1061 con 3, 4 o 5,. . . Sin embargo, los datos no coinciden con la primera tabla, según la cual hay 2857, 1060, . . . Esta diferencia se debe a que la función table() ignora por defecto los valores perdidos, pero tapply() los toma en consideración y los incorpora a cada una de las clases. Como en el data.frame resultados hay un caso, el 4838, con NA’s en casi todas las variables, la agrupación tabulada por tapply() da valores superiores en una unidad a los que corresponden eliminando los valores perdidos. Una manera de evitar esta situación, si así nos interesa, es eliminar previamente el caso problemático. Tablas bidimensionales Constituyen el ejemplo más típico de lo que entendemos por tablas de contingencia o “de referencias cruzadas” (en terminología de bases de datos). En el caso anterior, en el que estábamos interesados en el estudio de la variable número de insuficientes, una tabla bidimensional podría ser la que proporcionara el reparto de la variable en los diferentes años incluidos en el data.frame. Para conseguir esa tabla podríamos teclear table(Año,IN) resultando Año 1994 1995 1996 1997 1998 1999 2000 2001 IN 0 1 2 3 27 11 2 3 69 20 21 4 104 37 26 24 110 64 29 19 415 160 117 87 383 149 129 94 323 144 101 95 213 118 85 84 4 3 14 7 10 88 69 78 60 5 1 6 7 4 60 78 91 74 6 5 7 10 11 61 81 79 63 7 4 6 6 7 53 39 69 41 8 9 4 5 6 31 30 49 38 9 5 8 6 4 14 37 20 19 10 0 4 4 21 16 18 17 13 En las tablas de contingencia aparecen con frecuencia una columna y una fila con los valores totales por filas y columnas. En R no hay ninguna función que genere directamente tal tipo de tabla, pero resulta bastante sencillo generarla a partir de la tabla obtenida anteriormente. Los pasos serían los siguientes: (1) tabla.normal<-table(Año,IN) (2) total.filas<-apply(tabla.normal,1,sum) (3) tabla.intermedia<-cbind(tabla.normal,total.filas) (4) total.columnas<-apply(tabla.intermedia,2,sum) (5) tabla.final<-rbind(tabla.intermedia,total.columnas) En la primera instrucción se crea la tabla original y se asigna a un objeto que llamamos CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 48 tabla.normal. En la segunda, calculamos el total por filas con la función apply() (no confundir con tapply()). A apply() debemos indicarle varias cosas. En primer lugar, el objeto sobre el que aplicar la función (en este caso tabla.normal); en segundo, que se aplica por filas 1; y, finalmente, la acción que queremos aplicar (en este caso, calcular la suma, sum). El resultado de la función lo asignamos al objeto que llamamos total.filas. En la tercera instrucción incorporamos a la tabla inicial, mediante la función cbind(), la columna de totales calculada en el paso anterior. Asignamos el nuevo objeto a una tabla llamada tabla.intermedia que no nos interesará una vez terminado el proceso. En la cuarta, calculamos el total por columnas de la tabla intermedia, que contiene ya el total por filas. Lo hacemos con la función apply(), pero en este caso, como el cálculo se efectúa por columnas y no por filas, en lugar de poner como argumento el número 1, ponemos el 2. También, asignamos el resultado al objeto que llamamos total.columnas. En el último paso, incorporamos el total por columnas a la tabla intermedia con cbind() para obtener la tabla.final . El resultado sería: tabla.final 0 1 1994 27 11 1995 69 20 1996 104 37 1997 110 64 1998 415 160 1999 383 149 2000 323 144 2001 213 118 total.columnas1644 703 2 2 21 26 29 117 129 101 85 510 3 3 4 24 19 87 94 95 84 410 4 3 14 7 10 88 69 78 60 329 5 1 6 7 4 60 78 91 74 321 6 7 5 4 7 6 10 6 11 7 61 53 81 39 79 69 63 41 317 225 8 9 4 5 6 31 30 49 38 172 9 5 8 6 4 14 37 20 19 113 10 0 4 4 21 16 18 17 13 93 total.filas 70 163 263 285 1102 1107 1066 808 4837 Por supuesto, sería posible realizar la acción con una sola instrucción que reuniera las anteriores, pero la creación de macroinstrucciones debería reservarse a los casos en los que se tenga gran control de sus resultados. Como este tipo de tablas es muy habitual, podría ser conveniente crear una función que la generase con sólo indicar qué variables queremos tabular. Un ejemplo de función que llevaría a cabo ese cometido sería la siguiente. crea.tabla<-function(x){ +TF<-margin.table(x,1) + TC<-margin.table(x,2) rbind(cbind(x,TotalFilas=TF),TotalColumnas=c(TC,sum(TC))) } Como puede apreciarse, en este caso hemos utilizado la función margin(), que calcula directamente la suma por filas o columnas de manera similar a como lo hace apply(). La función se guardaría en el espacio de trabajo con el nombre crea.tabla. Ahora sólo habría que teclear crea.tabla(table(IN,Año)) para obtener la tabla anterior. 5.2. CÓMO RESUMIR LA INFORMACIÓN CONTENIDA EN UN DATA.FRAME 49 Del mismo modo, crea.tabla(table(Evaluacion,IN)) generaría una tabla de contingencia de las variables Evaluación e IN con totales de filas y columnas. Siempre que guardemos los objetos en el espacio de trabajo en el momento de abandonar R, dispondremos de la función crea.tabla() al abrir de nuevo ese espacio de trabajo. Sin embargo, la función no estaría disponible al abrir cualquier otro espacio de trabajo. Para que sí lo estuviera, podemos introducir la función en el paquete base tal y como se explicó en el apartado Medidas características. Tablas multidimensionales En las tablas multidimensionales se resume la información de más de dos variables. Por supuesto, para visualizarlas en un plano bidimensional, es preciso agruparlas en dos ejes. Supongamos que queremos conocer el número de calificaciones contenidas en nuestra tabla de resultados académicos agrupadas por año,curso y evaluación. Podríamos teclear ftable(Año,Curso,Evaluacion) saliendo por pantalla la siguiente tabla Evaluacion 1 Año Curso 1994 1 0 2 0 3 0 4 0 1995 1 0 2 0 3 0 4 0 1996 1 0 2 0 3 0 4 0 1997 1 0 2 0 3 0 4 0 1998 1 99 2 96 3 95 4 72 2 F 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 102 97 76 0 0 70 0 0 0 111 52 0 27 119 90 9 66 107 103 102 106 84 73 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 50 1999 1 2 3 4 2000 1 2 3 4 2001 1 2 3 4 103 132 44 65 90 110 100 56 95 146 102 58 103 138 82 66 97 120 101 59 97 148 102 60 104 133 72 65 100 121 69 43 0 0 0 0 Por supuesto, es posible agrupar alguna de las variables en clases tal y como hicimos en las tablas bidimensionales. Así, ftable(Año,Curso,cut(SB,3)) nos proporcionará una tabla con el número de sobresalientes agrupado en tres clases (cut(SB,3)) en los diferentes cursos de los años incluidos en la base de datos. Finalmente, indicar que podemos agrupar las variables mediante expresiones cuasialgebráicas que facilitarán la creación de tablas de dimensiones mayores. Si deseamos agrupar, por un lado, Curso y Año y, por otro, Evaluacion y SB. Podríamos introducir la siguiente instrucción ftable(Año+Curso~Evaluacion+SB)3 lo que generaría una tabla de cuatro dimensiones agrupadas en dos entradas. 5.2.2. Tablas con otras medidas estadísticas Además de las tablas de recuento de casos estudiadas en el apartado anterior, es habitual elaborar otros tipos de tablas. Las más comunes son, tal vez, las tablas de frecuencias relativas, pero también nos pueden interesar otras medidas estadísticas. En este apartado veremos algunas de las solcuiones que proporciona R para elaborar tablas de este tipo. Tablas de frecuencias relativas Para crear este tipo de tablas podemos utilizar, de nuevo, las posibilidades que ofrece R (y que son típicas de los sistemas UNIX) para llegar a un objetivo final mediante pasos intermedios. Veamos el procedimiento con un ejemplo. (1)tabla.inicial<-table(Año,Curso) (2)total.filas<-apply(tabla.inicial,1,sum) (3)str(tabla.inicial) (4)tabla.frecuencias.rel<-tabla.inicial[1:8,1:4]/total.filas*100 (5)options(digits=4) (6)tabla.frecuencias.rel Con la primera instrucción creamos una tabla bidimensional. 3 La tilde se introduce con la combinación de teclas AltGr + 4 5.2. CÓMO RESUMIR LA INFORMACIÓN CONTENIDA EN UN DATA.FRAME 51 Con la segunda, creamos el total por filas, tal y como se hizo en un apartado anterior, con la función apply() Con la tercera instrucción analizamos la estructura de la tabla inicial. En este caso nos informa que tabla.inicial es el resultado de tabular la variable Año (que presenta 8 elementos [1:8]) y la variable Curso (que presenta 4 elementos [1:4]). Estos datos son utilizados en la siguiente orden, que hace dividir de manera adecuada los elementos de tabla.inicial entre los elementos de total.filas para generar la nueva tabla tabla.frecuencias.rel. El resultado se multiplica por 100 para obtener porcentajes. Como se puede apreciar, la estructura de la tabla inicial se incorpora a la operación algebráica entre corchetes para efectuar la selección de los elementos de la tabla. La quinta es una instrucción símplemente para reducir el número de decimales de la salida (por defecto, digits=7). La última orden imprime por pantalla la tabla de frecuencias relativas calculadas en base al total por filas. La tabla es la siguiente Año 1994 1995 1996 1997 1998 1999 2000 2001 1 0.000 0.000 0.000 3.158 27.314 28.004 26.923 23.762 Curso 2 0.00 0.00 11.44 23.16 27.59 36.40 32.93 36.39 3 4 100.00 0.00 68.10 31.90 50.42 38.14 37.54 36.14 25.05 20.05 17.89 17.71 25.33 14.82 25.25 14.60 Afortunadamente, R nos proporciona la función prop.table() que nos genera directamente tablas de frecuencias relativas por filas o columnas. En el caso anterior, sólo deberíamos teclear prop.table(tabla.inicial,1)*100 para obtener el mismo resultado. Obsérvese que hay que multiplicar por 100 si deseamos que la tabla muestre porcentajes ya que por defecto el resultado aparece en tantos por uno. Si hubiéramos introducido la siguiente orden prop.table(tabla.inicial,2)*100 el resultado hubiera sido una tabla de frecuencias relativas por columnas (como manda el 2 introducido como argumento de la función). Otras tablas estadísticas Conocemos ya la función summary() y la manera de obtener otras medidas estadísticas de las variables incluidas en un data.frame. Por otro lado, en el apartado anterior hemos visto cómo utilizar la función apply() para calcular totales por filas y columnas. En este apartado vamos a profundizar un poco más en las posibilidades de extracción de información en forma de tablas. CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 52 Cuando nos interese obtener determinado estadístico de una serie de variables de un data.frame o una tabla bidimensional, podemos aplicar la función apply(). Por ejemplo, para conocer el rango de las variables IN,SF,B,NT y SB, podemos teclear apply(resultados[3:7],2,range,na.rm=T) la orden es ya familiar. Los corchetes seleccionan las variables que queremos analizar, el 2 ordena a la función que “trabaje” por columnas, range es la función que deseamos aplicar a las variables, y na.rm=T indica que se omitan los valores perdidos. El resultado es B [1,] 0 [2,] 9 IN 0 10 NT SB SF 0 0 0 9 10 10 La función también puede aplicarse a una tabla. Por ejemplo, podemos pedir la desviación estandard para las diferentes modalidades de la variable IN en los diferentes años del data.frame resultados. La tabla que hemos llamado tabla.final en un apartado anterior será ahora el argumento de la función apply(). Este es el resultado: apply(tabla.final,2,sd,na.rm=T) 0 1 2 3 4 5 6 7 8 9 10 total.filas 499.82 213.02155.81125.92101.66100.4997.8669.85 52.80 34.57 28.10 1474.60 Podemos incluso hacer tablas más complejas, en las que se presente el estadístico de una variable en una relación cruzada de dos variables diferentes. Por ejemplo, podemos querer conocer la media de insuficientes en los diferentes cursos a lo largo de los distintos años recogidos en nuestro data.frame. Como se ve, en este caso entran en juego tres variables. Dos de ellas, Año y Curso, serán las entradas de la tabla y la tercera, IN, será el valor. Para obtener este tipo de tablas aplicaremos la función tapply() de la siguiente manera tapply(IN,list(Año,Curso),mean) el resultado es 1994 1995 1996 1997 1998 1999 2000 2001 1 NA NA NA 1.89 2.11 2.26 2.26 2.83 2 NA NA 1.15 0.71 2.68 3.02 3.04 2.89 3 3.09 3.00 2.32 3.64 2.90 3.05 3.64 3.99 4 NA 1.06 1.70 1.89 1.49 1.76 2.51 1.81 Este tipo de tablas va bastante más allá que las simples tablas de frecuencias y proporcionan información solicitada muy habitualmente en los estudios estadísticos. Esta tabla nos dice, por ejemplo, que en 1996, la media de insuficientes en tercero fue de 2.32 y que en 2001 fue de 3.99. 5.2. CÓMO RESUMIR LA INFORMACIÓN CONTENIDA EN UN DATA.FRAME 53 5.2.3. Métodos gráficos para presentar la información contenida en tablas Además de las tablas, podemos utilizar diferentes gráficos para visualizar la información contenida en una base de datos. El tipo de representación dependerá del número de variables que queramos analizar y la naturaleza de las mismas. En apartados anteriores ya hemos presentado algunas de las funciones gráficas que podremos aplicar con este fin (hist(), plot(), boxplot(). Por ese motivo nos centraremos en la aplicación de la función plot() al caso concreto de tablas. Anteriormente hemos creado una tabla unidimensional con el número de casos que presentaban cada uno de los valores que adopta la variable IN. Podemos representar gráficamente la información contenida en esa tabla con la instrucción barplot(table(resultados$IN)) cuyo resultado es el gráfico de la Figura 5.10, un gráfico de barras, que resulta una opción muy adecuada a una variable de este tipo. Para el caso de tablas bidimensionales podemos utilizar la función plot(). Por ejemplo, si queremos representar el número de insuficientes a lo largo de los diferentes años, podemos teclear plot(table(IN,Año)) que proporciona el gráfico de la Figura 5.11. Este gráfico permite descubrir algunas particularidades a primera vista, como el hecho de que durante los cuatro primeros años haya bastantes menos registros que durante los cuatro segundos; la disminución progresiva del alumnado con cero insuficientes; o la gran cantidad de alumnos que tuvieron 10 insuficientes en el año 1997. Cuando la representación que queremos hace es de carácter multidimensional, la función plot() da lugar a un gráfico más complejo. Para ilustrarlo utilizaremos los datos contenidos en la tabla Titanic que viene con R. Ejecutando las siguientes instrucciones se obtiene la Figura 5.12. El gráfico requiere mayor esfuerzo de interpretación pues se representa por un lado el sexo de la persona y si sobrevivió o no y, por otro, la clase de pasaje y la edad. Puede apreciarse como la mayor mortandad afectó a los varones adultos de clase inferior y que la mayor tasa de supervivencia se dio entre las mujeres de primera clase. Lo hemos obtenido del siguiente modo data(Titanic) plot(Titanic) Logicamente, es posible representar parte de una tabla utilizando las técnicas de selección que ya conocemos. Por ejemplo, para analizar la evolución de la media de insuficientes en tercero a lo largo de los años podemos seleccionar la columna correspondiente de la última tabla elaborada. Para ello no habría mas que teclear plot(tapply(IN,list(Año,Curso),mean)[,3],type=“l”) En esta instrucción, con [,3] se le indica a R que seleccione todos los elementos de la tercera columna. Con type=“l” se le dice que el gráfico sea de líneas. El resultado es el de la Figura 5.13. Como puede observarse, en el eje de las abcisas aparece un índice en lugar del año. Como dijimos anteriormente, los retoques necesarios para hacer los CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 0 500 1000 1500 54 0 1 2 3 4 5 6 7 8 9 10 Figura 5.10: Gráfico de barras con barplot() 1998 1999 2000 2001 10 9 8 7 6 5 4 3 2 IN 1 0 1994 1995 1996 1997 Año Figura 5.11: Un gráfico interesante para tablas bidimensionales 5.2. CÓMO RESUMIR LA INFORMACIÓN CONTENIDA EN UN DATA.FRAME 55 1st Adult 2nd Child Adult Child 3rd Adult Child Crew Adult Female Yes No Sex Male Yes No Child Class Figura 5.12: Representación gráfica de una tabla multidimensional gráficos más elegantes se verán en el capítulo final. 56 4.0 3.5 3.0 tapply(IN, list(Año, Curso), mean)[, 3] 1 2 3 Index 5 6 7 8 CAPÍTULO 5. ESTADÍSTICA DESCRIPTIVA 4 Figura 5.13: Representación de parte de una tabla multidimensional 2.5 Capítulo 6 Pruebas de significación 6.1. Pruebas no paramétricas 6.1.1. Pruebas basadas en la χ2 Vamos a considerar la aplicación de la χ2 en dos situaciones: 1. para evaluar el ajuste de unos datos unidimensionales a una distribución de probabilidad 2. para determinar la homegeneidad de una variable a partir de una tabla de contingencia χ2 para analizar el ajuste a una distribución La función que proporciona R para la aplicación de los test basados en la χ2 es chisq.test(). Aplicada a un vector de datos sin más argumentos, evalua el ajuste de los mismos a una función de distribución uniforme. Por ejemplo, consideremos que en el centro escolar cuyos resultados estamos analizando el alumnado procede de cuatro barrios diferentes. En el Cuadro 6.1 se indican el número de alumnos/as que escolariza de cada barrio así como el número de alumnos/as con problemas educativos que aporta cada uno de los barrios. Supongamos que estamos interesados en saber si la procedencia del alumnado por barrios es unidorme con un nivel de significación del 1 %. Para ello podríamos introducir Número de alumnos/as Alumnos/as con dificultades A 195 84 B 90 54 C 28 11 D 23 11 Cuadro 6.1: Alumnado total y con problemas educativos 57 58 CAPÍTULO 6. PRUEBAS DE SIGNIFICACIÓN la siguiente instrucción chisq.test(c(195,90,28,23)) cuyo resultado es Chi-squared test for given probabilities data: c(195, 90, 28, 23) X-squared = 228.7381, df = 3, p-value = < 2.2e-16 Como la probabilidad asociada al parámetro χ2 p-value = < 2.2e-16 es inferior al nivel de significación (0.01), se rechaza la hipótesis de uniformidad en la procedencia del alumnado (tal y como podíamos prever a la luz de los datos proporcionados). Pasemos ahora al análisis del ajuste a una distribución de probabilidad no uniforme. En el ejemplo que estamos considerando, podríamos estudiar si el alumnado con dificultades educativas se distribuye entre los barrios de procedencia en concordancia con el número total de alumnos/as que viene de cada barrio. En este caso tendremos que evaluar la χ2 incorporando como frecuencias teóricas la probabilidad de procedencia que le corresponde a cada barrio, que es el cociente entre el alumnado del barrio y el alumnado total. Podríamos actuar del siguiente modo alumnado<-c(195,90,28,23) prob.alumnado<-alumnado/sum(alumnado) alum.dificultades<-c(84,54,11,11) chisq.test(alum.dificultades,p=prob.alumnado) en la primera sentencia se asigna los datos de procedencia de los diferentes sectores a alumnado. En la segunda se calcula la probabilidad asociada a cada barrio dividiendo el número de alumnos/as de cada barrio entre el total (sum(alumnado)). En la tercera se asigna a alum.dificultades el número de alumnos con dificultades educativas procedentes de cada barrio en el orden adecuado. En la cuarta se aplica el test de χ2 al alumnado con dificultades tomando como probabilidades teóricas las incluidas en el vector prob.alumnado. Hay que notar que la asignación de un nuevo vector de probabilidades se hace con el argumento p. El resultado Chi-squared test for given probabilities data: al.problemas X-squared = 4.1505, df = 3, p-value = 0.2457 nos muestra que la distribución por barrios del alumnado con dificultades no se desvía significativamente de la teórica pues la probabilidad de que los valores observados se ajusten a los teóricos, 0.2457, es muy superior al 0.05 o 0.01 que suelen tomarse como niveles de significación para rechazar la hipótesis nula. Test de χ2 para tablas de contingencia Este tipo de pruebas suele utilizarse para analizar la homogeneidad de una variable respecto a otra. Consideremos el siguiente caso: en el centro escolar se han recogido los datos de la Tabla 6.2 que representa el número de alumnos/as que supera/repite el primer curso de bachillerato en relación al número de áreas que suspendió el curso precedente (0,1,2 o más). Si analizamos la tabla, parece claro que no tienen las mismas probabilidades de susperar el curso los alumnos que aprobaron todas las áreas que los que promocionaron con alguna área suspensa, pero deseamos poner a prueba estadísti- 6.1. PRUEBAS NO PARAMÉTRICAS 59 camente esta suposición. Podríamos hacerlo mediante la χ2 de la siguiente manera promocionan<-c(101,14,7) repiten<-c(14,25,13) tabla3<-data.frame(promocionan,repiten,row.names=c(“0”,”1”,”2+”)) Con las dos primeras instrucciones creamos los vectores que formarán las columnas con valores de la tabla de contingencia. Con la tercera creamos un data.frame, llamado tabla3, en el que introducimos como nombres de filas (row.names) los correspondientes a 0,1,2+ suspensos. Una vez creada la tabla de contingencia, podemos aplicar el test mediante chisq.test(tabla3) cuya salida es Pearson’s Chi-squared test data: tabla3 X-squared = 50.777, df = 2, p-value = 9.417e-12 el valor de p = 9,417e − 1 permite rechazar claramente la hipótesis de homogeneidad de la variable repetir/no repetir en relación con la variable número de suspensos en el curso anterior lo que demuestra claramente que, a la hora de aprobar o repetir el primer curso de bachillerato, no es lo mismo haber aprobado la ESO con 0, 1 o 2 o más áreas suspensas. 6.1.2. Test de Wilcoxon, de Mann-Whitney y de Kolmogorov-Smirnov La función wilcox.test(), permite aplicar varias pruebas de significación: el test de rangos signados de Wilcoxon; el contraste de suma de rangos de Wilcoxon; y el test de la U de Mann-Whitney. Las diferencias entre ellos radican en los argumentos que se pasen a la función: si tenemos dos muestras y se incorpora la opción paired=T, tendremos muestras emparejadas y el test aplicado será el de suma de rangos de Wilcoxon. Si dejamos la opción por defecto, que es paired=F, el test aplicado será el de Mann-Whitney. Si sólo hay una muestra, podemos utilizar la función como contraste de localización incorporando como argumento el valor(mu) de la mediana que queremos contrastar. Nosotros nos centraremos en la aplicación de la función para contrastar la hipótesis de que dos muestras provengan, o no, de la misma distribución. Supongamos que en nuestra centro educativo se ha definido un índice algebráico para medir el rendimiento académico del alumnado y se quiere contrastar su adecuación Suspensos curso anterior 0 1 2+ Promocionan 101 14 7 Repiten 14 25 13 Cuadro 6.2: Promoción con suspensos 60 CAPÍTULO 6. PRUEBAS DE SIGNIFICACIÓN comparando los resultados obtenidos con el índice con las valoraciones realizadas por el profesorado. En el Cuadro 6.3 se pueden ver los resultados obtenidos por los dos medios. Para llevar a cabo la prueba tecleamos: profes<-c(1,4,1,1,2,2,3,4,5,3,2,5,4,3,2,3,2,4,4,4) indice<-c(1,4,1,1,2,1,3,4,5,3,2,5,5,3,2,3,2,5,4,3) wilcox.test(profes,indice,paired=T) En las dos primeras instrucciones se asignan los valores de las muestras a contrastar, y en la tercera se aplica la función a las mismas, indicando que se trata de muestras emparejadas. El resultado es Wilcoxon signed rank test with continuity correction data: profes and indice V = 5, p-value = 1 alternative hypothesis: true mu is not equal to 0 Warning messages: 1: Cannot compute exact p-value with ties in: wilcox.test.default(profes, indice, paired = T) 2: Cannot compute exact p-value with zeroes in: wilcox.test.default(profes, indice, paired = T) a pesar de los dos mensajes que da, informando de los problemas encontrados, queda de manifiesto que la hipótesis nula, que establece el mismo origen de las dos muestras, queda totalmente confirmada ya que la probabilidad de que así sea es máxima (p = 1). A la misma conclusión llegamos aplicando el test de Kolmogorov-Smirnov para dos muestras. La función de R encargada de esta prueba es ks.test(). En el ejemplo que nos ocupa, deberíamos teclear ks.test(profes,indice) cuyo resultado es Two-sample Kolmogorov-Smirnov test data: profes and indice D = 0.1, p-value = 1 alternative hypothesis: two.sided 6.1.3. Test de Kruskal-Wallis Con esta prueba podemos contrastar la procedencia (o no) de la misma distribución de tres o más muestras. Supongamos que queremos saber si el índice de resultados académicos contenido en el data.frame resultados ha variado significativamente desde el año 1994 a 1996. Aplicando la función kruskal.test() a los valores correspondientes podremos contestar la pregunta. El procedimiento podría ser el siguiente: a<-subset(resultados,resultados$Año==1994,select=10) b<-subset(resultados,resultados$Año==1995,select=10) c<-subset(resultados,resultados$Año==1996,select=10) kruskal.test(list(a[,1],b[,1],c[,1])) Con las tres primeras sentencias se selecciona el índice que queremos analizar (la columna 10) para los años que nos interesan y se asignan a tres data.frame llamados a,b,c. En la cuarta sentencia se aplica el test de Kruskal-Wallis. Para ello tenemos que cons- 6.1. PRUEBAS NO PARAMÉTRICAS Alumno/a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Valoración profesorado 1 4 1 1 2 2 3 4 5 3 2 5 4 3 2 3 2 4 4 4 61 Valoración índice 1 4 1 1 2 1 3 4 5 3 2 5 5 3 2 3 2 5 4 3 Cuadro 6.3: Valoraciones alumnado 62 CAPÍTULO 6. PRUEBAS DE SIGNIFICACIÓN truir una lista encadenando las tres series de datos. Como están en sendos data.frames, es preciso seleccionar la columna (en estos casos, única) que los almacena. El resultado Kruskal-Wallis rank sum test data: list(a[, 1], b[, 1], c[, 1]) Kruskal-Wallis chi-squared = 2.1531, df = 2, p-value = 0.3408 nos indica que no podemos rechazar la hipótesis de que las tres muestras provengan de la misma distribución, pues la probabilidad de que así sea, p − value = 0,3408, es superior al nivel de significación del 5 %. La función boxplot() nos proporciona una aproximación gráfica a este tipo de análisis como puede apreciarse si tecleamos boxplot(a[,1],b[,1],c[,1], notch=T) el resultado es la Figura 6.1 en la que se aprecia que el primer cuartil ha aumentado a lo largo de los tres años y que el rango intercuartílico se ha ido reduciendo. A pesar de ello, las medianas son similares y, como nos indican las cuñas laterales introducidas por la opción notch=T, no difieren entre sí de manera significativa al nivel de significación del 5 % puesto que las incisiones se solapan unas con otras. 6.2. Pruebas paramétricas 6.2.1. Test basados en la t de Student Debemos abandonar el data.frame resultados para hacer una pequeña incursión en las pruebas paramétricas. Este tipo de pruebas sólo pueden ser aplicadas bajo la condición de normalidad y con variables medidas en escala de intervalo o de razón, y ninguna de las contenidas en él cumple esas condiciones. Para ilustrar el uso de la función t.test(), utilizaremos los datos contenidos en el data.frame survey del paquete MASS. Para cargarlo tecleamos library(MASS) data(survey) attach(survey) para obtener una descripción de los datos teclearemos ?survey En el data.frame tenemos varias variables. Supongamos que nos interesa estudiar la estatura, que es una variable que se distribuye normalmente y se mide en una escala de razón, y su relación con el sexo y el carácter zurdo o diestro de los estudiantes. Para ver si hay diferencias por sexos, teclearíamos t.test(Height~Sex) que le dice a R que aplique el test a la variable altura diferenciando dos muestras en función del sexo (es lo que indicamos con la tilde que enlaza ambas variables) De nuevo, como hemos puesto los datos en la ruta de búsqueda con attach(survey), podemos hacer referencia directa al nombre de las variables. El resultado que sale por pantalla es 6.2. PRUEBAS PARAMÉTRICAS −20 −10 0 10 20 30 40 63 1 2 3 Figura 6.1: Notched boxplots Welch Two Sample t-test data: Height by Sex t = -12.9243, df = 192.703, p-value = < 2.2e-16 alternative hypothesis: true difference in means is not equal to 0 95 percent confidence interval: -15.14454 -11.13420 sample estimates: mean in group Female mean in group Male 165.6867 178.8260 el valor de p =< 2,2e − 16 nos indica que la hipótesis alternativa, es decir, que las medias de estatura de las dos muestras difieren significativamente, es perfectamente aceptable. Por el contrario, las diferencias entre los zurdos y los diestros no son significativas, ya que t.test(Height~ W.Hnd) da un p − value = 0,2807. 64 CAPÍTULO 6. PRUEBAS DE SIGNIFICACIÓN Capítulo 7 Correlación y regresión Continuaremos con los datos del data.frame survey para explicar los procedimientos básicos de los análisis de correlación y de regresión. A lo largo del capítulo se presentarán procedimientos gráficos algo más sofisticados que los estudiados hasta ahora. En este caso serán explicados en el momento de su aplicación ya que son inseparables del contenido conceptual de la materia. Serán dejados para el capítulo final los procedimientos de carácter general válidos para todo tipo de gráficos. 7.1. Estudio de la correlación bivariada 7.1.1. Métodos gráficos La primera aproximación al estudio de la relación entre variables es el análisis visual del diagrama de dispersión de ambas que, como ya se sabe, puede obtenerse con la función plot(). En la Fig 7.1 se puede observar la relación entre el pulso y la estatura de los individuos del data.frame survey, como puede apreciarse, la correlación es escasa. En la Fig 7.2, por el contrario, se observa una elevada correlación positiva entre la estatura y el tamaño de la mano. En ocasiones, al estudiar la relación entre dos variables, nos interesa agrupar los casos en función de una tercera variable, generalmente categórica (lo que en R se llama una factor). Podemos optar por dos soluciones: representar en el mismo gráfico todos los casos representando cada grupo con un símbolo y/o color distinto; o representar por separado los dos grupos. Veamos qué podemos hacer en R. Supongamos que deseamos estudiar la relación entre la estatura y el tamaño de la mano pero queremos diferenciar en el diagrama de dispersión los puntos en función del sexo de la persona. Una forma sencilla de conseguirlo sería tecleando plot(Height,NW.Hnd,pch=as.integer(Sex)) obsérvese que hemos añadido a la función un argumento (pch) que gestiona el tipo de símbolo que aparece en el gráfico. Cada símbolo tiene asociado un número, por ese motivo se transforma el código del sexo, que en el data.frame está como factor, en un 65 CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 70 40 50 60 Pulse 80 90 100 66 150 160 170 180 190 200 Height 18 16 14 NW.Hnd 20 22 Figura 7.1: Un ejemplo de escasa correlación 150 160 170 180 190 200 Height Figura 7.2: En este caso, la correlación es fuerte 7.1. ESTUDIO DE LA CORRELACIÓN BIVARIADA 67 número. En este caso asociará al sexo femenino el 1 y al masculino el 2. El resultado es la Figura 7.3 en la que vemos que aparecen triángulos y circunferencias diferenciando los sexos. Sin embargo, este tipo de gráficos precisa una leyenda pues, de lo contrario, no sabremos qué símbolo está asociado a cada grupo. Para crear un gráfico con leyenda deberíamos teclear plot(Height,NW.Hnd,pch=as.integer(Sex)) legend(locator(1),legend=c(“Chicas”,“Chicos”),pch=as.integer(Sex)) La primera instrucción es la que ya hemos utilizado anteriormente: crea el gráfico y lo presenta en la pantalla gráfica. La segunda es la que añadirá la leyenda y debemos analizarla en detalle. La función utilizada es legend(), una de las funciones de tipo interactivo que encontramos en R y que permite realizar acciones con el ratón sobre la pantalla gráfica. Con el argumento locator(1) se indica que haremos sobre la pantalla un solo clic con el botón izquierdo del ratón allí donde queramos situar la leyenda. El argumento legend=c(“Chicas”,“Chicos”) indica los nombres de los grupos representados en el gráfico. Es muy importante ponerlos en el mismo orden en el que se encuentran en el diseño del data.frame, lo cual puede conocerse con la función str(). Hay que darse cuenta de que R no puede adivinar, qué entendemos nosotros por Chico o Chica de modo que tendremos que decírselo explícitamente. Por último, pch=as.integer(Sex) define el símbolo que utilizaremos en la leyenda que debe ser, evidentemente, el mismo que hayamos utilizado al hacer el gráfico. Tras teclear la instrucción, la linea de comandos pasa el control al ratón. Deberemos hacer clic en el gráfico en la zona adecuada y veremos cómo, efectivamente, se inserta la leyenda. El resultado es el que podemos ver en la Figura 7.4. Este tipo de gráficos puede ser en ocasiones un poco lioso y tal vez nos interese separar los dos grupos en gráficos separados pero en la misma figura. Para ello podemos utilizar la función coplot(), diseñada para hacer gráficos condicionados. En este caso queremos representar la relación entre dos variables controlando la salida gráfica en función del sexo de los registros. Para obtener dicho gráfico podemos teclear coplot(Height~NW.Hnd | Sex) Obsérvese que la tilde indica qué variables queremos analizar, y la barra (|) nos indica qué variable queremos controlar. El resultado es muy interesante y podemos verlo en la Figura 7.5. Por supuesto, es posible introducir más de una variable de control. Por ejemplo, si queremos condicionar los diagramas de dispersión no sólo por el sexo, sino también por el carácter diestro o zurdo del individuo, deberíamos teclear coplot(Height~NW.Hnd | Sex + W.Hnd) como se vé en la expresión, los factores condicionantes se encadenan con el signo +. El resultado será un gráfico 2x2 como el de la Figura 7.6. Para acabar con el análisis gráfico de la correlación entre variables comentaremos el gráfico que presenta diagramas de dispersión cruzados para varias variables. Podemos crearlos con la función pairs(). Así, pairs(survey) generaría un gráfico que permitiría apreciar las relaciones entre todas las variables del CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 18 14 16 NW.Hnd 20 22 68 150 160 170 180 190 200 Height 18 16 Chicas Chicos 14 NW.Hnd 20 22 Figura 7.3: Diagrama de dispersión con dos grupos de datos 150 160 170 180 190 200 Height Figura 7.4: Diagrama mucho más elaborado 7.1. ESTUDIO DE LA CORRELACIÓN BIVARIADA 69 Given : Sex Male Female 16 18 20 22 180 170 150 160 Height 190 200 14 14 16 18 20 22 NW.Hnd Figura 7.5: Diagrama de dispersión con dos grupos de datos obtenido con coplot() Given : Sex Male Female 16 18 20 22 Given : W.Hnd 150 200 150 160 170 Left 180 190 Height 160 170 Right 180 190 200 14 14 16 18 20 22 NW.Hnd Figura 7.6: Diagrama de dispersión controlando para dos variables CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 70 data.frame. Muchas de ellas no son numericas y por lo tanto su interés es menor, pero la representación, en general, facilita el análisis exploratorio de la correlación entre variables. Para observar las correlaciones entre las variables numéricas podemos crear un data.frame con las mismas y pasarle la función pairs(). Así, pairs(data.frame(Pulse,Height,NW.Hnd,Wr.Hnd) crea la Figura 7.7, que permite apreciar las relaciones entre todas las variables numéricas de este data.frame como, por ejemplo, la clara relación entre el tamaño de ambas manos o la menor que hay entre el tamaño de la mano y la estatura. 7.1.2. Matrices de correlaciones La función cor() proporciona la matriz de coeficientes de correlación de Pearson de las variables introducidas como argumento. Así, cor(survey$Height,survey$Wr.Hnd, use=”complete.obs”) nos da el coeficiente de correlación entre la estatura y el tamaño de la mano. El resultado [1] 0.600991 es lo que deberemos interpretar. Hay que hacer notar que, cuando hay valores perdidos, a la función cor() hay que decirle a R qué hacer con los mismos. Para que haga el cálculo del coeficiente sólo a partir de los casos completos para ambas variables introducimos la opción use=”complete.obs”. Para calcular el coeficiente de determinación sólo deberíamos elevar al cuadrado el valor devuelto por la función. Por ejemplo tecleando cor(survey$Height,survey$Wr.Hnd, use=”complete.obs”)^2 Adviértase que, como no hemos asignado el valor a ningún objeto, no quedará almacenado en el espacio de trabajo; tan sólo será presentado por pantalla. Podemos aplicar la función cor() a un data.frame con variables numéricas. En este caso obtendremos una matriz de correlaciones. Para obtener la matriz de correlaciones de las cuatro variables numéricas contenidas en el data.frame survey podemos teclear options(digits=3) cor(data.frame(NW.Hnd,Wr.Hnd,Height,Pulse),use=”complete.obs”) como se ve, se crea un data.frame interno que almacena las variables numéricas. Este será el objeto que recibirá la acción de la función. Como antes, sólo se tendrán en cuenta los casos completos. El resultado es NW.Hnd Wr.Hnd Height Pulse NW.Hnd 1.0000 0.9664 0.6000 -0.0170 Wr.Hnd 0.96643 1.00000 0.61835 -0.00761 Height 0.6000 0.6184 1.0000 -0.0847 Pulse -0.01698 -0.00761 -0.08468 1.00000 Como se explicó en un capítulo anterior, si nos interesase calcular estos valores con asiduidad, sería conveniente definir sendas funciones. 7.1. ESTUDIO DE LA CORRELACIÓN BIVARIADA 18 22 150 170 190 22 14 71 22 14 18 Wr.Hnd 80 100 14 18 NW.Hnd 190 40 60 Pulse 150 170 Height 14 18 22 40 60 80 100 Figura 7.7: Diagrama de dispersión cruzada entre variables con pairs() CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 72 Una cuestión importante es contrastar el grado de significación de los coeficientes de la matriz de correlaciones. Para un α √ = 0,05, podemos considerar que son significativos los valores ri j que cumplen |ri j | · n > 1,96 siendo n el número de casos de la serie de datos1 . En el ejemplo que estamos utilizando, para obtener el valor de n debemos tener en cuenta que en el cálculo de los coeficientes de correlación estamos considerando sólo los casos completos para las cuatro variables numéricas. Para sabér cuántos son, podemos teclear str(na.omit((data.frame(NW.Hnd,Wr.Hnd,Height,Pulse)))) que solicita la estructura del data.frame de variables numéricas omitiendo(cosa que conseguimos con na.omit())los casos con algún valor perdido. El resultado ‘data.frame’: 170 obs. of 4 variables: $ NW.Hnd: num 18 20.5 20 17.7 17.7 17.3 19.5 18.5 17.2 20.2 ... $ Wr.Hnd: num 18.5 19.5 20 18 17.7 17 20 18.5 17 19.5 ... $ Height: num 173 178 165 173 183 ... $ Pulse : num 92 104 35 64 83 74 72 90 80 66 ... - attr(*, "na.action")=Class ’omit’ Named int [1:67] 3 4 12 13 15 16 19 25 26 29 ... .. ..- attr(*, "names")= chr [1:67] "341213"... nos informa de que el data.frame consta de 170 observaciones. En este momento podemos multiplicar los coeficientes de correlación por la raiz cuadrada del número de observaciones efectuando la siguiente operación cor(data.frame(NW.Hnd,Wr.Hnd,Height,Pulse), use=”complete.obs”)*sqrt(170) cuyo resultado es Pulse -0.22136976 -0.09920578 -1.10414082 13.03840481 √ Puede apreciarse que la diagonal se ha transformado en la 170 y que son significativos al 5 % los coeficientes de correlación entre las variables tamaño de la mano escritora/tamaño de la mano no escritora, r = 12,600 y altura frente a tamaño de ambas manos (r = 7,823 y r = 8,062). NW.Hnd Wr.Hnd Height Pulse NW.Hnd 13.0384048 12.6007250 7.8225652 -0.2213698 Wr.Hnd 12.60072500 13.03840481 8.06234371 -0.09920578 Height 7.822565 8.062344 13.038405 -1.104141 7.1.3. Coeficiente de correlación de Spearman Veamos ahora cómo calcular el coeficiente de correlación de Spearman. Este está basado en rangos y es aplicable, por lo tanto, a variables de tipo ordinal. Como en nuestro data.frame no tenemos variables ordinales, crearemos dos con la función cut(), que divide un vector en partes iguales. Así, a<-na.omit(cut(NW.Hnd,5)) b<-na.omit(cut(Wr.Hnd,5)) reclasificará esas dos variables a sendas variables ordinales con cinco modalidades 1 En el caso de α = 0,01, el valor que debería ser superado es 2,575 7.1. ESTUDIO DE LA CORRELACIÓN BIVARIADA 73 (1, 2, 3, 4, 5) y además eliminará los valores perdidos. Para calcular el coeficiente de correlación de Spearman sólo habrá que teclear cor(rank(a),rank(b)) dando como resultado [1] 0.8834653 7.1.4. Coeficiente de correlación parcial Finalmente, veremos cómo calcular el coeficiente de correlación parcial entre dos variables controlando los efectos que ejerce una tercera variable sobre la relación entre ellas. Este coeficiente de correlación parcial se suele utilizar cuando se supone que puedan existir relaciones espúreas entre variables. Si definimos el coeficiente de correlación parcial entre las variables a, b controlado por la variable c como rab − rac · rbc q rab|c = p 2 2 · 1 − rac 1 − rbc y tenemos tres variables a,b,c, podremos calcular rab|c con la siguiente serie de instrucciones. r.ab<-cor(a,b) r.bc<-cor(b,c) r.ac<-cor(a,c) r.abc<-(r.ab-(r.ac*r.bc))/(sqrt(1-(r.ac^2))*sqrt(1-(r.bc^2))) Con las tres primeras se calculan los coeficientes de correlación bivariada entre las tres variables tomadas dos a dos. En la cuarta se introduce la expresión algebraica que permite obtener el coeficiente de correlación parcial. La siguiente función, bautizada como cor.par() nos daría el coeficiente con sólo introducir las variables en orden consecutivo, siendo las dos primeras las variables a estudiar, y la tercera la variable que queremos controlar. cor.par<-function(a,b,c){ r.ab<-cor(a,b) r.bc<-cor(b,c) r.ac<-cor(a,c) coef<-(r.ab-(r.ac*r.bc))/(sqrt(1-(r.ac^2))*sqrt(1-(r.bc^2))) return(coef) } Para ilustrar el uso de los índices de correlación parcial consideraremos los datos del Cuadro 7.1. En él aparecen una serie de variables (temperatura, albedo, flujo térmico, radio y distacia media al Sol) para los cuatro planetas gigantes del Sistema Solar. En primer lugar deberemos introducir los datos de la tabla en un data.frame al que podemos llamar, por ejemplo s.solar. Ahora, supongamos que nos interesa conocer los factores que influyen en la temperatura de los planetas. En primer lugar, desearemos confirmar que exite relación entre ésta y la distancia al Sol. Para ello podríamos teclear CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 74 Planeta Júpiter Saturno Urano Neptuno Distancia(u.a.) 5,203 9,539 19,18 30,06 Temperatura(K) 125 94 58 55 Radio(Km) 71714 60330 26200 25225 Albedo 0,42 0,36 0,37 0,33 Flujo térmico(erg/cm2sg) 7600 2800 175 285 Cuadro 7.1: Algunos datos de los planetas gigantes cor(s.solar$dist,s.solar$temp) que da [1] -0.9085313 es decir, una elevada correlación negativa; a mayor alejamiento del Sol, menor temperatura planetaria. Para analizar gráficamente esta relación podemos teclear plot(s.solar$dist,s.solar$temp) lo que genera la Figura 7.8 en la que se observa que hay un descenso de la temperatura con la distancia claramente exponencial. Si queremos conocer las relaciones entre todas las variables tomadas de dos en dos, deberemos solicitar la matriz de correlaciones, para lo cual sólo tenemos que cor(s.solar). El resultado es temp albedo flujo dist radio temp albedo flujo dist radio 1.0000000 0.8361303 0.9774232 -0.9085313 0.9808471 0.8361303 1.0000000 0.8731807 -0.8367831 0.7406799 0.9774232 0.8731807 1.0000000 -0.8295449 0.9198315 -0.9085313 -0.8367831 -0.8295449 1.0000000 -0.9220161 0.9808471 0.7406799 0.9198315 -0.9220161 1.0000000 Puede observarse que los coeficientes son elevados para casi todos los pares de variables, llamando especialmente la atención la relación entre la temperatura y el radio planetario y el flujo térmico. En la Figura 7.9 vemos gráficamente la relación entre la temperatura y el flujo térmico. Aparentemente, la distancia al Sol no es el factor que más afecta a la temperatura de estos planetas. Para conocer cuál es la correlación entre temperatura y distancia, “descontando” el efecto del flujo térmico, podemos utilizar nuestra función cor.par() del siguiente modo attach(s.solar) cor.par(temp,dist,flujo) el resultado [1] -0.828139 refleja que, cuando controlamos el efecto del flujo térmico, la relación entre temperatura y distancia al Sol disminuye. Si lo analizamos en términos del coeficiente de determinación (cuadrado del coeficiente de correlación), pasamos de que la distancia explique el 83 % de la varianza a que explique sólo el 69 %. Los astrónomos conocen bien este hecho. Los planetas gaseosos están tan lejos del Sol 7.1. ESTUDIO DE LA CORRELACIÓN BIVARIADA 90 60 70 80 temp 100 110 120 75 5 10 15 20 25 30 dist 4000 0 2000 flujo 6000 Figura 7.8: Relación entre temperatura y distancia 60 70 80 90 100 110 120 temp Figura 7.9: Relación entre temperatura y flujo térmico 76 CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN que, incluso, emiten al espacio más energía de la que reciben del astro rey. Por ese motivo, su temperatura depende en gran medida del aporte de calor interno. Esto queda claramente de manifiesto al analizar la relación entre estas dos variables removiendo el efecto de la distancia como vemos a continuación cor.par(temp,flujo,dist) [1] -0.9589847 Comparando este valor con el valor de la matriz de correlaciones, apreciamos que controlar para la distancia no conlleva una reducción muy grande del coeficiente de correlación entre temperatura y flujo térmico. 7.2. Regresión lineal simple En esta sección trabajaremos con los datos contenidos en el data.frame trees. La siguiente sucesión de órdenes nos permiten cargarlos, conocer su contenido y ponerlo en ruta de búsqueda. data(trees) str(trees) ?trees attach(trees) Vemos que consiste en una serie de datos de altura, diámetro y volumen de 31 árboles. Como en todo análisis de regresión simple, intentaremos analizar las relaciones existentes entre las variables con la intención de predecir valores de un de ellas en función de otra. Antes de comenzar, haremos un análisis preliminar de las correlaciones bivariadas. Con cor(trees) obtendremos la siguiente matriz de correlaciones Girth Height Volume Girth 1.0000000 0.5192801 0.9671194 Height 0.5192801 1.0000000 0.5982497 Volume0.9671194 0.5982497 1.0000000 Con pairs(trees) obtendremos la Figura 7.10. Tanto en la matriz como en la figura se puede apreciar que hay una relación lineal muy fuerte entre el volumen del árbol y su diámetro. También se observa que la relación entre el volumen y la altura no lo es tanto. Finalmente, hay también relación entre el diámetro y la altura. En R, para efectuar el análisis de regresión linear se utiliza la función lm(). Esta genera un tipo especial de objeto que puede ser argumento de muchas funciones que podremos utilizar para obtener información sobre el modelo de regresión calculado. Por ese motivo, es conveniente asignar el resultado de lm() a algún objeto con su correspondiente nombre. En nuestro caso, para hacer la regresión simple entre volumen y diámetro teclearemos 7.2. REGRESIÓN LINEAL SIMPLE 70 75 80 85 18 65 77 80 85 8 10 14 Girth 50 70 65 70 75 Height 10 30 Volume 8 10 14 18 10 30 50 70 Figura 7.10: Relación entre las variables de trees lm.vol.dia<-lm(Volume~Girth) lo que almacena en el objeto lm.vol.dia todos los resultados del análisis de regresión. Como puede observarse, lm() utiliza el sistema de fórmulas (con la tilde) para expresar la variable dependiente y la independiente. La función summary() aplicada al objeto de regresión, nos va a proporcionar la mayor parte de la información que nos interesa en un principio como vemos a continuación summary(lm.vol.dia) Call: lm(formula = Volume Girth) Residuals: Min 1Q Median 3Q Max -8.0654 -3.1067 0.1520 3.4948 9.5868 Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) -36.9435 3.3651 -10.98 7.62e-12 *** Girth 5.0659 0.2474 20.48 < 2e-16 *** — Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 4.252 on 29 degrees of freedom Multiple R-Squared: 0.9353, Adjusted R-squared: 0.9331 F-statistic: 419.4 on 1 and 29 DF, p-value: 0 Tanto el valor como el grado de significación los coeficientes de la recta de regresión aparecen en el apartado coefficients. En la penúltima linea aparecen los valores de r y r2 ; y, en la última, la probabilidad de que el modelo obtenido sea inadecuado. CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 78 En este caso, el coeficiente de determinación (r2 ) nos indica que el 93 % de la variación en el volumen del árbol puede explicarse por variaciones en el diámetro del mismo. Por otro lado, el modelo de regresión obtenido es apropiado puesto que la probabilidad de que no lo sea es cero. Para hacer predicciones del volumen de una serie de árboles a partir de su diámetro debemos crear un data.frame con una columna que almacene los diámetros de los mismos. Por ejemplo, si tenemos tres ejemplares, A,B,C con diámetros 11.5,10.6,18.7, para obtener los volúmenes predichos habría que proceder como sigue. muestra<-data.frame(Girth=c(11.5,10.6,18.7), row.names=c(“A”;”B”;”C”)) predict(lm.vol.dia,muestra) Con la primera instrucción creamos el data.frame muestra que contiene una columna con el nombre de la variable independiente (en este caso el diámetro) que alberga los valores de la muestra. Además se indica la etiqueta de cada uno de los registros (A,B,C). Con la segunda instrucción pedimos que calcule los valores predichos según el modelo guardado en lm.vol.dia. El resultado es A B C 21.31389 16.75462 57.78806 Si, además del valor predicho, deseamos conocer el intervalo de confianza a un nivel determinado (digamos del 95 %), deberemos teclear predict(lm.vol.dia,muestra,level=.95,interval=“confidence”) el resultado será A B C fit lwr upr 21.3138919.51889 23.10889 16.7546214.69672 18.81252 57.7880654.61832 60.95779 donde podemos conocer el valor inferior (lwr) y superior (upr) para ese nivel de confianza. Para modificar el nivel de confianza, sólo hay que introducir otro valor en el argumento level de la función predict(). Finalmente, vamos a ver cómo incorporar al diagrama de dispersión algunos elementos importantes en análisis de regresión. El primero es la recta de regresión. Para ello vamos a utilizar la función abline() que, pasada por consola tras haber generado un gráfico, le incorpora una recta con los parámetros que se le indiquen como argumentos. Veamos cómo hacerlo si queremos estudiar la relación entre el volumen y la altura. plot(Height,Volume) abline(lm(Volume~Height)) El resultado se presenta en la Figura 7.11. En muchas ocasiones sería conveniente identificar ciertos puntos del diagrama de dispersión que destacan por alguna circunstancia (en general, valores anómalos). Para hacer esto podemos utlizar la función identify() de la siguiente manera plot(Height,Volume) 7.2. REGRESIÓN LINEAL SIMPLE Volume 10 20 30 40 50 60 70 79 65 70 75 80 85 Height Figura 7.11: Inclusión de la recta de regresión con abline() identify(Height,Volume) Tras haber realizado el gráfico con plot(), tecleamos la función identify() aplicada al mismo. Esto es así porque la función interactúa con el dispositivo gráfico. Tras teclearla, deberemos utilizar el ratón para marcar con el botón izquierdo los puntos que deseamos etiquetar. Por defecto se añadirá al gráfico un número índice, pero esto puede modificarse si existe alguna otra etiqueta. Cuando hayamos concluido, deberemos hacer clic con el botón derecho sobre el gráfico para cerrar la función. En ese momento aparacerán sobre la consola los índices de los puntos seleccionados. En la Figura 7.12 puede apreciarse cómo se ha seleccionado y etiquetado algunos de los casos. CAPÍTULO 7. CORRELACIÓN Y REGRESIÓN 80 30 40 50 28 20 18 6 10 Volume 60 70 31 65 70 75 80 85 Height Figura 7.12: Identificación de casos con identify() Capítulo 8 Personalización de gráficos R posee unas capacidades gráficas extraordinarias. En este (último) capítulo vamos a estudiar las más básicas, las que nos van a permitir personalizar nuestros gráficos y dotarlos de una mayor presencia. Para ello nos serviremos de los gráficos que hemos ido presentando a lo largo del manual y cuya presentación no ha sido la que desearíamos para presentar en una publicación. 8.1. Títulos Consideremos la Figura 3.3 de la página 20. Lo primero que llama la atención es que los títulos de los ejes están en inglés. Ello se debe a que el data.frame cars viene con R. Posiblemente, con datos nuestros eso no ocurrirá, pero aún así es conveniente saber cómo añadir/modificar los títulos de una figura. Consideraremos tanto el texto en sí como su formato (fuente y color). Para los títulos de ejes tenemos los argumentos x.lab e y.lab. Tras ellos sólo hay que teclear entrecomillado el texto que queremos que vaya a cada uno de los ejes. Así plot(speed,dist,x.lab=“Velocidad“,y.lab=”Distancia“) cambiaría los nombres de las variables del inglés al castellano. Si queremos, podemos cambiar la fuente del texto con el argumento font.lab, que acepta cuatro valores que van del 1 al 4. Para que aparezcan en negrita habría que añadir a la sentencia anterior font.lab=2. También podemos cambiar el color. Posteriormente hablaremos de cómo conseguir millones de colores diferentes, ahora nos conformaremos con las posibilidades que nos dan los primeros diez números enteros. Para ello utilizaremos el argumento col.lab seguido de un número del 0 al 9. Para el azul, el número es el 4. También podemos modificar el tamaño de la fuente con cex.lab. Este argumento acepta valores numéricos, resultando los menores a 1 en una reducción del tamaño y, los superiores a 1, en un aumento. Así, plot(speed,dist,x.lab=“Velocidad“,y.lab=”Distancia“,font.lab=2, col.lab=4,cex.lab=1.5) nos dejerá unos títulos en castellano, en negrita,de mayor tamaño, y de color azul. 81 82 CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS Para añadir un título al gráfico utilizaremos el argumento main seguido del título entrecomillado. Al igual que con los títulos de los ejes, podremos utilizar los argumentos col.main, cex.main y font.main para modificar el color, el tamaño y la fuente del título principal. El resultado final de la personalización de títulos puede apreciarse en la Figura 8.1, y resulta de la siguiente instrucción plot(cars$speed,cars$dist,xlab="Velocidad",ylab="Distancia", font.lab=3,col.lab=4,cex.lab=1.5,main="Diagrama de dispersión\nVelocidad/Distancia", cex.main=2,col.main=4) Obsérvese que, para introducir un salto de linea en el título principal se ha tecleado \n allí donde se desea que aparezca. 8.2. Ejes El aspecto de los ejes puede modificarse de la misma manera que hemos comentado en los títulos con los argumentos font.axis, col.axis y cex.axis. También podemos modificar el rango de los ejes con los argumentos xlim e ylim que, como puede adivinarse, establecen los límites para cada uno de los ejes. Así, xlim=c(0,40),ylim=c(20,45) generáría un eje de abcsisas de 0 a 40 y un eje de ordenadas de 20 a 45. Además de modificar el rango, se puede variar la escala de los ejes. Con el argumento log podemos transformar a escala logarítmica el eje x, el y o ambos. Con la siguiente orden modificaríamos los dos ejes de la Figura 7.8 (página 75) para transformarlos a escala logarítmica y obtener la Figura 8.2. plot(s.solar$dist,s.solar$temp,log="xy",xlab="log(distancia)", ylab="log(temp)",col.axis=4,fg=2) Para modificar sólo uno de los ejes lo único que hay que hacer es notificárselo al argumento. Así, log=“x” sólo pasaría a logarítmico el eje de abcisas, y log“y” haría lo propio sólo con el eje de ordenadas. Hemos visto que en algunos casos (por ejemplo en la Figura 5.2, página 39) en el eje de abcisas no nos aparece el nombre de la variable sino que aparece un númeroíndice. En este caso en concreto nos convendría que apareciera en lugar de 1, IN, en lugar de 2, SF, ... Para conseguirlo podemos utilizar la función axis(), que modifica las etiquetas de los ejes como vemos a continuación. Para obtener la Figura 8.3 deberíamos introducir la siguiente secuencia de instrucciones attach(resultados) boxplot(IN,SF,B,NT,SB,xaxt="n") axis(1,at=1:5,lab=c("IN","SF","B","NT","SB"),col.axis=2) La primera ya la conocemos bien; nos evitará hacer continuamente llamadas al data.frame en las siguientes instrucciones. Con la segunda obtenemos un boxplot múltiple en el que no visualizaremos las etiquetas del eje de abcisas porque hemos añadido la opción xaxt=“n” (del inglés x axis text, no). Estas etiquetas las añadimos con la tercera instrucción. A la función axis() se le indica con el 1 que va a trabajar con el eje de abcisas. Con at=1:5 se le dice que debe colocar las primeras cinco etiquetas, que son las cinco que indicamos con lab=c("IN","SF","B","NT","SB"). Finalmente le indicamos que las coloree de rojo simplemente para destacar el efecto de la función 8.2. EJES 83 60 0 20 40 Distancia 80 100 120 Diagrama de dispersión Velocidad/Distancia 5 10 15 20 25 Velocidad 80 70 60 log(temp) 90 100 110 120 Figura 8.1: Personalización de títulos 5 10 15 20 log(distancia) Figura 8.2: Ejes logarítmicos 25 30 84 CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS sobre el gráfico original. 8.3. Colores Como hemos visto, podemos personalizar los colores de fuentes y ejes. Por supuesto, podemos establecer un color para el objeto principal de la representación (puntos, líneas, polígonos) con el argumento col. Asímismo,podemos modificar el color de fondo (bg) y de los objetos situados en primer plano (fg). Con las funciones rgb() y hsv() aplicadas a cualquier argumento que indique un color, podemos conseguir millones de colores. Tan sólo hay que introducir como parámetros de esas funciones tres valores decimales comprendidos entre el 0 y el 1. En la primera (rgb()), será una mezcla de rojo, verde y azul. En la segunda (hsv()), son los valores de matiz, saturación y valor. En la Figura 8.4 podemos ver el resultado de la siguiente instrucción hist(cars$dist,col=rgb(.3,.6,.2),fg=2,main="Histograma", col.main=hsv(.2,.8,.9),xlab="velocidad",ylab="frecuencia", col.lab=hsv(.2,.8,.6)). Puede verse cómo han cambiado tanto el color del histograma como de los títulos. También hay que hacer notar que el argumento fg=2 determina que el color de los ejes sea rojo. 8.4. Añadir textos a un gráfico Esta es una operación habitual en gráficos. Los textos nos permiten realizar comentarios o resaltar información. Veamos cómo utilizar la función text() para añadir un pequeño comentario a la Figura 3.3. Podríamos hacer lo siguiente plot(cars$speed,cars$dist) text(5,100,"Se puede apreciar una\nbuena correlación\nentre ambas variables,\nespecialmente para velocidades\nbajas y medias.", adj=0,col=4) Con la primera instrucción creamos la figura. La segunda es la que debemos analizar en profundidad. En primer lugar le decimos a R dónde debe colocar el texto. Para ello nos basamos en las coordenadas utilizadas en el gráfico. Como vemos que en la zona de bajas velocidades y elevadas distancias hay bastante espacio en blanco, ponemos 5,100 (5 de velocidad y 100 de distancia). A continuación introducimos el texto entrecomillado (obsérvese la inserción de nuevas lineas con \n). Con adj=0 indicamos que queremos un texto justificado a la izquierda (por defecto es centrado). Finalmente, le decimos que queremos un texto en color azul con col=4. El resultado es el de la Figura 8.5. Para establecer las coordenadas que definen la localización de los textos también podemos utilizar funciones. Por ejemplo, si quisiéramos etiquetar los elementos principales del boxplot de la Figura 5.1 (página 39) podríamos proceder de la siguiente manera boxplot(cars$speed) 8.4. AÑADIR TEXTOS A UN GRÁFICO 85 Figura 8.3: Modificar etiquetas de ejes con axis() 10 5 0 frecuencia 15 Histograma 0 5 10 15 20 25 velocidad Figura 8.4: Millones de colores con rgb() y hsv() CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS Se puede apreciar una buena correlación entre ambas variables, especialmente para velocidades bajas y medias. 60 0 20 40 cars$dist 80 100 120 86 5 10 15 20 25 cars$speed Figura 8.5: Añadir textos con text() axis(1,at=1,"Velocidad",cex.axis=1.5,col.axis=2) text(1.3,fivenum(cars$speed)[1],"mínimo",col=4,adj=0) text(1.3,fivenum(cars$speed)[2],"primer cuartil",col=4,adj=0) text(1.3,median(cars$speed),"mediana",col=4,adj=0) text(1.3,fivenum(cars$speed)[4],"tercer cuartil",col=4,adj=0) text(1.3,fivenum(cars$speed)[5],"máximo",col=4,adj=0) Como vemos, tras dibujar el gráfico y añadirle la etiqueta Velocidad al eje de abcisas, se procede a incorporar textos a los elementos del boxplot. Para que aparezca a un lado de la caja y no encima de ella, la primera coordenada la establecemos en 1.3. Para situar el texto mínimo a la altura exacta, no hace falta obtener previamente ese valor mínimo, podemos establecer su coordenada y seleccionando el primer elemento del vector proporcionado por la función fivenum(), que da el mínimo, primer cuartil, mediana, tercer cuartil y máximo, de una variable. Para colocar los demás textos sólo habrá que ir seleccionando los correspondientes elementos de ese vector como se hace en las sentencias 4,5,6 y 7. El resultado es el que se aprecia en la Figura 8.6. 8.5. Tipos de puntos y lineas Podemos modificar tanto el tipo de linea o punto, como su color y tamaño. Por ejemplo, la Fig 5.7 representa una gráfica de tipo linea obtenida con la instrucción plot(density(cars$speed)). Vamos a ver qué argumentos debemos utilizar para modificarla y obtener la Figura 8.7. La instrucción requerida es plot(density(cars$speed),lty=6,lwd=4,col=4) El argumento col establecerá el color. El tipo de línea lo definimos con lty, que acepta 8.5. TIPOS DE PUNTOS Y LINEAS 25 87 20 máximo 15 tercer cuartil mediana 5 10 primer cuartil mínimo Velocidad Figura 8.6: Utilizar funciones para establecer coordenadas de textos con text() Density 0.00 0.01 0.02 0.03 0.04 0.05 0.06 density(x = cars$speed) 0 5 10 15 20 25 30 N = 50 Bandwidth = 2.15 Figura 8.7: Personalización de líneas con lty,lwd,col CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS 88 varios valores numéricos. De la misma manera estableceremos la anchura de la linea con lwd . Para gráficos de puntos el procedimiento es muy similar. Para transformar la Figura 3.3 en la Figura 8.8 debemos teclear plot(cars$speed,cars$dist,pch=3,cex=.5,col=2) Podemos ver que volvemos a dar color con col. Con pch, que acepta valores numéricos, establecemos el tipo de símbolo, correspondiendo el 3 a la cruz. Finalmente, con cex introducimos un factor de disminución o aumento del tamaño con respecto al que está definido por defecto. 8.6. Añadir puntos y lineas a un gráfico En R es posible superponer puntos y lineas a gráficos recien creados en la pantalla gráfica. Por ejemplo, supongamos que queremos añadir al diagrama de dispersión de la Figura 3.3 unos puntos que indiquen los cuartiles y la media de ambas variables. El resultado (Figura 8.9), se obtiene de la siguiente manera plot(cars$speed,cars$dist) points(fivenum(cars$speed)[c(2,3,4)],fivenum(cars$dist)[c(2,3,4)], pch=18,cex=2,col=2) points(mean(cars$speed),mean(cars$dist),pch=15,cex=2,col=4) Con la segunda instrucción añadimos el primer cuartil, la mediana y el tercer cuartil1 de una variable frente a los de la otra, en forma de rombos rojos. Con la tercera, añadimos la media de speed frente a la media de dist y la representamos como un cuadrado azul. En la sección correspondiente a la regresión, hemos visto cómo añadir la linea de ajuste por mínimos cuadrados a un diagrama de dispersión con la función abline(). En este apartado veremos otras posibilidades en relación a la adición de curvas y líneas a gráficos. Volvamos de nuevo al gráfico de cars. Imaginemos que deseamos añadir dos lineas que indiquen los valores medios de vada variable, de modo que el gráfico quede dividido en cuatro sectores (valores inferiores a las dos medias, inferiores a una pero superior a la otra, y superiores a las dos medias). Podríamos hacerlo como sigue plot(cars$speed,cars$dist) abline(h=mean(cars$dist),lty=3,col=4) abline(v=mean(cars$speed),lty=3,col=4) Con la segunda orden, mandamos hacer una linea horizontal que pase por la media de la variable dist. Con la tercera hacemos una horizontal que pase por la media de speed. El resultado lo vemos en la Figura 8.10. Veamos, finalmente, cómo añadir a un gráfico una función matemática. Como ejemplo tomaremos la Figura 5.6 de la página 43. En ella se puede observar un histograma al que se le ha superpuesto la función de distribución normal teórica para su contraste visual. Se ha obtenido mediante la siguiente serie de instrucciones hist(scale(cars$speed),prob=T) 1 Que son el segundo, tercer y cuarto elemento, respectivamente, del vector devuelto por fivenum() 8.6. AÑADIR PUNTOS Y LINEAS A UN GRÁFICO 60 0 20 40 cars$dist 80 100 120 89 5 10 15 20 25 cars$speed 60 40 20 0 cars$dist 80 100 120 Figura 8.8: Personalización de puntos con pch,cex,col 5 10 15 20 cars$speed Figura 8.9: Añadir puntos con points() 25 CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS 60 0 20 40 cars$dist 80 100 120 90 5 10 15 20 25 cars$speed Figura 8.10: Añadir lineas con abline() curve(dnorm(x),add=T) Con la primera obtenemos el histograma de la variable estandarizada. Con la segunda se le superpone la distribución teórica para lo cual se utiliza la función dnorm(). Para una variable estandarizada, la gráfica se obtiene con dnorm(x). Hay que hacer notar el add=T que se incorpora para que la función se superponga al gráfico preexistente. También podemos utilizar estas herramientas para añadir a la función de densidad de la Figura 5.7 (página 43) la función teórica y obtener la Figura 8.11. Las instrucciones para ello serían plot(density(scale(cars$speed)),col=4,lwd=2,ylim=c(0,0.5)) curve(dnorm(x),add=T,col=2,lwd=2) Con la primera dibujaremos la función de densidad de la variable cars$speed estandarizada en color azul. Con la segunda le superpondremos la función de densidad teórica en color rojo. 8.7. Plantillas gráficas De las secciones anteriores se deduce que, si nuestra intención es utilizar con frecuencia determinada configuración gráfica (porque nos gusta un tipo determinado de caracter, color, grosor de linea,...), sería muy conveniente almacenarlo de alguna manera para evitarnos teclear continuamente esa serie de parámetros. Eso es lo que vamos a poder hacer con la función par(). Antes de crear una nueva configuración, puede ser conveniente guardar la configuración por defecto con un nombre concreto. Así, 8.7. PLANTILLAS GRÁFICAS 91 0.0 0.1 0.2 Density 0.3 0.4 0.5 density(x = scale(cars$speed)) −3 −2 −1 0 1 2 3 N = 50 Bandwidth = 0.4066 Figura 8.11: Añadir curvas con curve() defecto<-par() guardará todos esos parámetros en el objeto llamado defecto. Ahora, podríamos querer definir una configuración nueva como la siguiente col=3,pch=2,lwd=2,fg=2,bg=hsv(.4,.1,.8),cex=.7 cuyo significado conocemos ya. La siguiente secuencia de órdenes generará la Figura 8.12, la Figura 8.13 y la Figura 8.14. plantilla<-par(col=3,pch=2,lwd=4,fg=2,bg=hsv(.4,.1,.8),cex=.7) hist(cars$speed) hist(cars$speed,col=8) plot(cars$speed,cars$dist,col=7) par(defecto) Con la primera instrucción definimos la nueva plantilla gráfica y se abre automáticamente un dispositivo gráfico si no estaba abierto ninguno. Con la segunda generamos el primer gráfico sin pasar como argumento ningún parámetro gráfico. Con la tercera mantenemos todos los parámetros de plantilla pero introducimos un color que rellenará las barras del histograma. Con la cuarta generamos un diagrama de dispersión de las dos variables. Como se ve, no se indica el tipo de punto ni su color porque estamos bajo los efectos de plantilla. Finalmente, con la última (par(defecto)) lo que hacemos es desactivar la plantilla y volver a los parámetros por defecto. A partir de este momento los gráficos volverán a crearse con los parámetros normales. Para cargar de nuevo los de nuestra plantilla deberemos teclear par(plantilla) CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS 92 0 5 Frequency 10 15 Histogram of cars$speed 0 5 10 15 20 25 cars$speed Figura 8.12: Modificación de parámetros gráficos con par() (I) 0 5 Frequency 10 15 Histogram of cars$speed 0 5 10 15 20 25 cars$speed Figura 8.13: Modificación de parámetros gráficos con par() (II) 8.8. GRÁFICOS MÚLTIPLES 60 0 20 40 cars$dist 80 100 120 93 5 10 15 20 25 cars$speed Figura 8.14: Modificación de parámetros gráficos con par() (III) 8.8. Gráficos múltiples Por lo general presentaremos sólo un gráfico por figura, pero en ocasiones nos puede interesar presentar varios gráficos agrupados en una misma figura (por ejemplo, dos histogramas de sendas variables). Para hacer esto, R dispone de varias posibilidades. Una de ellas es la función layout(). Esta función requiere como argumento una matriz en la que se informe del número de ventanas en las que hay que dividir el dispositivo gráfico, su numeración y el número de columnas y filas que vamos a utilizar. Por ejemplo, para dividir la figura en seis ventanas (cada una para un gráfico diferente) agrupadas en dos filas de tres columnas, deberemos teclear layout(matrix(1:6,2,3)) los que dividirá la pantalla tal y como vemos en la Figura 8.15. La matriz que introducimos como argumento incluye en primer lugar el número de ventanas que vamos a crear y su numeración (en este caso serán seis consecutivas, 1:6). En segundo lugar se indica el número de filas 2 y, finalmente el de columnas 3. Puede apreciarse en la figura el orden en el que serán ocupadas las ventanas cuando desde R se mande realizar un gráfico. Por defecto se numeran desde la esquina superior izquierda y siguiendo las columnas. Las posibilidades de subdivisión son inmensas; en realidad, van más allá de lo que necesitaremos en la práctica. Podemos. por ejemplo, hacer divisiones desiguales de la pantalla sólo con asignar a ventanas consecutivas la misma numeración. Por ejemplo, layout(matrix(c(1,1,2,3,4,4),2,3)) CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS 94 1 3 5 2 4 6 Figura 8.15: División de la pantalla gráfica con layout() genera la división de la Figura 8.16. Puede resultar una buena idea tener creadas unas cuantas matrices para las divisiones que utilicemos más habitualmente. Por ejemplo, crear las siguientes matrices dos.hor<-matrix(1:2,1,2) dos.ver<-matrix(1:2,2,1) cuatro<-matrix(1:4,2,2) normal<-matrix(1) nos permitirá dividir las pantallas en dos ventanas horizontales, dos verticales, cuatro ventanas, y volver a la pantalla única original con sólo teclear layout(dos.hor) layout(dos.ver) layout(cuatro) layout(normal) Para crear la Figura 8.17, podríamos introducir las siguientes órdenes X11() layout(dos.ver) hist(cars$speed,main="Velocidad",col=2,xlab="V(mph)", ylab="Frecuencia") hist(cars$dist,main="Distancia",col=4,xlab="d(pies)", ylab="Frecuencia") Para crear layouts regulares (con todas las ventanas del mismo tamaño) también podemos utilizar un parámetro de la función par(); se trata de mfrow. Este nos permite establecer el número de ventanas en términos de filas y columnas. Así, para dividir el 8.8. GRÁFICOS MÚLTIPLES 95 2 1 4 3 Figura 8.16: División desigual de la pantalla gráfica con layout() Distancia 15 10 0 5 Frecuencia 10 5 0 Frecuencia 15 Velocidad 0 5 10 V(mph) 20 0 40 80 d(pies) Figura 8.17: Dos histogramas con layout() 120 96 CAPÍTULO 8. PERSONALIZACIÓN DE GRÁFICOS dispositivo gráfico en seis ventanas organizadas en tres columnas de dos filas habría que teclear par(mfrow=c(2,3)) lo que, tras la siguiente serie de instrucciones, permite obtener la Figura 8.18. plot(density(scale(cars$speed)),col=4,lwd=2,ylim=c(0,0.5), main="Densidad(velocidad)",ylab="densidad",xlab=) hist(cars$speed,col=3, main="Histograma(velocidad)", ylab="frecuencia",xlab="velocidad") plot(cars$speed,cars$dist,main="Dispersión(vel/dist)", xlab="velocidad",ylab="distancia") plot(density(scale(cars$dist)),col=2,lwd=2,ylim=c(0,0.5), main="Densidad(distancia)",xlab=,ylab="densidad") hist(cars$dist,col=6,main="Histograma(distancia)", xlab="distancia",ylab="frecuencia") plot(cars$dist,cars$speed,main="Dispersión(dist/vel)", xlab="distancia",ylab="velocidad") 8.8. GRÁFICOS MÚLTIPLES 97 Histograma(velocidad) Dispersión(vel/dist) −1 0 1 2 3 0 5 10 15 20 25 5 10 15 20 25 velocidad velocidad Histograma(distancia) Dispersión(dist/vel) 15 velocidad 10 10 0 0.0 5 0.1 5 0.2 frecuencia 0.3 20 0.4 15 25 0.5 Densidad(distancia) densidad 60 80 −3 0 0 0.0 20 0.1 5 40 distancia 10 frecuencia 0.3 0.2 densidad 0.4 100 15 0.5 120 Densidad(velocidad) −2 0 1 2 3 4 0 20 40 60 80 120 0 distancia Figura 8.18: Figuras múltiples par(mfrow) 20 40 60 80 distancia 120