1. OBJETIVO DE LA SESIÓN

El objetivo de sesión es familiarizarnos con los conceptos básicos de R. ¿Qué es un objeto en R? ¿Con qué clases/tipos de objetos se trabaja en R? A lo largo de este tutorial aprenderemos a definir vectores y operar con ellos; a crear dataframes y listas; a seleccionar elementos, añadir filas y columnas, etc. Como lo que se pretende es que se entienda la filosofía y la práctica del trabajo con R, todos los conceptos que se introducen se ilustran con ejemplos muy sencillos.

IMPORTANTE: Los materiales de esta sesión (v.1.0) fueron compilados el día 2022-05-25 y pueden ser susceptibles de modificación (corrección de errores, ampliación de contenidos, etc.)

2. SELECCIÓN DE CONTENIDOS BÁSICOS

2.1. Trabajar con proyectos

Una vez hemos entrado en la aplicación RStudio, deberíamos establecer el directorio de trabajo para indicar a R y RStudio donde se encuentran nuestros datos, scripts, etc. Sin embargo, la mejor opción es crear un proyecto. Al crear un proyecto todos los ficheros (datos, scripts, etc.) y carpetas quedan vinculados directamente al proyecto. Es decir, todo el trabajo que realicemos en un proyecto estará auto-contenido en el directorio del proyecto. De esta forma, podemos compartir fácilmente nuestro proyecto con alguien más o podemos copiar y pegar nuestro proyecto en otro ordenador, etc.

Para crear un proyecto en RStudio, seleccionamos File > New project… Se abrirá una ventana similar a la que se muestra en la siguiente Figura.


Para crear un proyecto en un nuevo directorio, hacemos clic en el botón New Directory. Seguidamente, seleccionamos el tipo de proyecto, en nuestro caso New Project. Ahora, asignamos un nombre al directorio (carpeta) que se va a crear y que al mismo tiempo será el nombre del proyecto de R. Para terminar, hacemos clic en el botón Create Project. Al seguir este proceso se habrá creado una carpeta en Documentos y dentro encontraremos el archivo: “nombre_carpeta.Rproj”.

Para crear un proyecto en una carpeta que ya existe, hacemos clic en el botón Existing Directory y después seleccionamos el directorio o carpeta ayudándonos del botón Browse…. Una vez elegida la carpeta, clicamos en el botón Create Project.

Para abrir un proyecto, hacemos doble clic sobre el archivo con extensión .Rproj. También podemos abrir el proyecto desde el menú de RStudio: File > Open Project…

Ventaja de los proyectos: cualquier fichero que guardemos trabajando en un proyecto se guardará en la carpeta del proyecto.

Aquí os dejo la dirección de dos lecturas cortas sobre los proyectos, la segunda incluye información sobre cómo crear proyectos.

Es muy conveniente crear un proyecto para cada trabajo que realicemos

Resulta muy muy interesante la versión cloud de Rstudio.

2.2. Conceptos básicos de R-base.

Una vez nos hemos familiarizado con el entorno de trabajo, vamos a ver algunos conceptos básicos de Rbase. En esta sesión vamos a estudiar los diferentes tipos de datos y objetos con los que trabja R.

Podéis descargaros una cheat cheet de R-base AQUÍ.

Vamos a comenzar utilizando R como una “super” calculadora, aunque evidentemente no es para esto para lo que vamos utilizarlo!!!

3 + 4
## [1] 7
log(10)
## [1] 2.302585
exp(-0.5)
## [1] 0.6065307
x <- 3 + 4  
x     # x es un vector cuya primera componente es 7. Enseguida vamos con los vectores!
## [1] 7
y = 2 + 6
y
## [1] 8
z <- c(x,y)     # la función c() se utiliza para crear vectores
z
## [1] 7 8
mean(z)
## [1] 7.5
a <- mean(z)
a
## [1] 7.5
b <- max(z)
b
## [1] 8
(c <- rnorm(100))
##   [1]  0.658337269 -1.823699878  0.656066332  1.626400176  0.552710696
##   [6]  0.489949066 -0.610055227 -0.687793878 -0.574567961 -1.411507564
##  [11] -0.160898532  0.062583949 -1.030248777 -0.566534377 -0.059569798
##  [16] -1.017336829 -0.390878649 -0.596385598 -1.240563688 -0.139637610
##  [21]  0.635036743  2.748488926 -0.899111260 -0.542562056 -0.286057057
##  [26]  0.093102473  0.609689565  1.043090640 -1.105063883 -1.962102913
##  [31] -0.106589558 -0.133779420  0.896911467  1.025840540 -0.726619521
##  [36] -0.708171806 -0.190012711  0.430037451 -0.075749489  0.520905739
##  [41]  0.037000040  1.561663901 -0.348234363  0.499224358 -1.128088600
##  [46] -0.539430614  1.491325930 -0.595689577  1.057958380  2.313787303
##  [51] -0.380816821  0.037234160  0.488502110 -1.325373231  0.045303282
##  [56]  0.748586485 -1.717895449  1.410176153 -0.231126550 -0.637108929
##  [61] -0.653383613  1.591560760 -0.930702419 -1.031758390  1.441355886
##  [66] -0.439093010 -1.561975839  0.004480816  1.262158492 -0.928587790
##  [71] -1.974084719 -0.370504730 -0.751366149 -1.313326290  2.336728722
##  [76] -0.348579783  0.497249766 -0.600609271 -0.414019718 -1.288218384
##  [81] -0.487018732  0.239247408 -0.633723717  0.040596073  0.433448990
##  [86] -0.328987310 -1.448917977 -0.665957125  0.830225386  0.332963801
##  [91] -0.942015238  1.234585000  0.587720280  0.262533522  1.072257516
##  [96]  0.412543482 -0.525088700 -0.213682043 -0.924833065 -0.021371084
round(c, 2)
##   [1]  0.66 -1.82  0.66  1.63  0.55  0.49 -0.61 -0.69 -0.57 -1.41 -0.16  0.06
##  [13] -1.03 -0.57 -0.06 -1.02 -0.39 -0.60 -1.24 -0.14  0.64  2.75 -0.90 -0.54
##  [25] -0.29  0.09  0.61  1.04 -1.11 -1.96 -0.11 -0.13  0.90  1.03 -0.73 -0.71
##  [37] -0.19  0.43 -0.08  0.52  0.04  1.56 -0.35  0.50 -1.13 -0.54  1.49 -0.60
##  [49]  1.06  2.31 -0.38  0.04  0.49 -1.33  0.05  0.75 -1.72  1.41 -0.23 -0.64
##  [61] -0.65  1.59 -0.93 -1.03  1.44 -0.44 -1.56  0.00  1.26 -0.93 -1.97 -0.37
##  [73] -0.75 -1.31  2.34 -0.35  0.50 -0.60 -0.41 -1.29 -0.49  0.24 -0.63  0.04
##  [85]  0.43 -0.33 -1.45 -0.67  0.83  0.33 -0.94  1.23  0.59  0.26  1.07  0.41
##  [97] -0.53 -0.21 -0.92 -0.02
set.seed(10)
d <- round(rnorm(100),2)

R utiliza funciones para realizar operaciones. En el ejemplo anterior son funciones: log(), exp(), c(), mean(), rnorm(), set.seed(), etc. Para utilizar una función deben especificarse unos determinados argumentos que los escribimos dentro de los paréntesis separados por comas. En el caso de la función round() hemos especificado dos argumentos: el vector que queremos redondear (c) y el número de decimales del redondeo (digits); en el caso de rnorm() sólo hemos especificado el número de datos que queríamos, el resto de argumentos son fijados a los valores por defecto.

El símbolo <- es el operador para asignar. También se puede utilizar = (o menos frecuente ->), aunque es preferible utilizar el <-.

El símbolo # se utiliza para introducir un comentario. Todo lo que quede a la derecha de # no se ejecutará.

Cuando se realiza una asignación se obtiene un objeto. Podemos ver el resultado o contenido de un objeto de varias formas. Por ejemplo, para ver qué es el objeto x podemos escribir en la consola:

  • x
  • print(x)
  • (x <- 3 + 4)

También lo podemos ver en el panel de entorno del escritorio de RStudio.

2.2.1. Instalar y cargar paquetes

R está compuesto por un sistema base, pero para extender su funcionalidad es necesario instalar paquetes adicionales.

Podemos instalar paquetes de varias formas:

  • A través del menú: Tools > Install packages…

  • En el escritorio de RStudio: Packages/Install. Vemos los paquetes que tenemos actualmente instalados y aquellos que se encuentran cargados.

Figura. Listado paquetes instalados en R


  • Utilizando la función install.packages(). El nombre del paquete que queremos instalar debe ir entre comillas.
install.packages("rio") # rio es un paquete que se utiliza para importar/exportar datos.

En ocasiones, para nuestra sesión de trabajo necesitamos instalar varios paquetes.

install.packages(c("dplyr","ggplot2","rio"))   

También es muy habitual instalar paquetes que se encuentran en GitHub (generalmente, versiones de desarrollo). Para hacer esto, primero tenemos que tener instalado el paquete devtools y luego …

# install.packages("devtools")
devtools::install_github("perezp44/personal.pjp")

Una vez instalado el paquete, hay que cargarlo para poderlo utilizar. Esto se hace con la función library().

library(rio) # el nombre del paquete se puede poner entre comillas o se pueden omitir.

2.2.2. Importar/exportar datos

Aprovechando que hemos instalado y cargado el paquete rio, vamos a ver cómo cargar datos (en distintos formatos) en R. En una sesión posterior veremos con más detenimiento la tarea de importar/exportar datos.

En este sesión vamos a utilizar el paquete rio porque cargar y guardar datos este paquete es muy fácil. Para cargar datos se utiliza la función import() y para guardar la función export().

Aquí tenemos un ejemplo:

df <- import("https://www.uv.es/vcoll/curso_R_sfp/precio_casas.xlsx")

df está formado por 546 observaciones de 12 variables. Para ver la estrucutra de un conjunto de datos (dataframe) puede utilizarse la función str()

str(df)
## 'data.frame':    546 obs. of  12 variables:
##  $ price     : num  42000 38500 49500 60500 61000 66000 66000 69000 83800 88500 ...
##  $ lotsize   : num  5850 4000 3060 6650 6360 4160 3880 4160 4800 5500 ...
##  $ bedrooms  : num  3 2 3 3 2 3 3 3 3 3 ...
##  $ bathrooms : num  1 1 1 1 1 1 2 1 1 2 ...
##  $ stories   : num  2 1 1 2 1 1 2 3 1 4 ...
##  $ driveway  : chr  "yes" "yes" "yes" "yes" ...
##  $ recreation: chr  "no" "no" "no" "yes" ...
##  $ fullbase  : chr  "yes" "no" "no" "no" ...
##  $ gasheat   : chr  "no" "no" "no" "no" ...
##  $ aircon    : chr  "no" "no" "no" "no" ...
##  $ garage    : num  1 0 0 0 0 0 2 0 0 1 ...
##  $ prefer    : chr  "no" "no" "no" "no" ...

2.2.3. Tipos de datos

¿Qué tipos de datos tenemos en R?

  • Caracteres (character)
  • Numéricos (numeric)
  • Enteros (integer)
  • Complejos (complex)
  • Lógicos (logic)
  • Factores (factor)

Veamos algunos ejemplos…

x <- c(1,2,3,4)    # con la función c() creamos el vector x
class(x)           # la función class() devuelve el tipo de objeto
## [1] "numeric"

y <- c("a","b")
class(y)
## [1] "character"

z <- c(1L,2L,3L)   # escribimos L detrás del número para obligar a que sea entero
class(z)
## [1] "integer"

w <- c(TRUE, F)    # en general, puede escribirse TRUE/FALSE o T/F
class(w)
## [1] "logical"

t <- c(1+2i, 1+3i)
class(t)
## [1] "complex"

k <- factor(x)    # convierte el vector x en un factor
k
## [1] 1 2 3 4
## Levels: 1 2 3 4
class(k)
## [1] "factor"

En los ejemplos anteriores hemos definido un vector en el que todos sus elementos eran del mismo tipo. Pero….¿qué pasa si tenemos los siguientes vectores?

x <- c(1,2,"a")
y <- c(FALSE, 1)
z <- c("a",T)

¿De qué tipo son ahora los vectores x, y, z?

class(x)
## [1] "character"
class(y)
## [1] "numeric"
class(z)
## [1] "character"

R ha forzado a que todos los elementos del vector sean del mismo tipo. A esto se le llama implicit coercion. Fijémonos cúal es el resultado de los vectores que hemos definido antes.

x 
## [1] "1" "2" "a"
y
## [1] 0 1
z
## [1] "a"    "TRUE"

En ocasiones somos nosotros los que estamos interesados en forzar que todos los elementos del vector sean del mismo tipo (esto es la explicit coercion). Para ello utilizamos las funciones as.numeric() , as.character(), as.logical() … Si el resultado no tiene sentido R producirá un mensaje de error o warning. Un ejemplo:

x <- c(1,2,"a")
x
## [1] "1" "2" "a"
as.numeric(x)
## Warning: NAs introducidos por coerción
## [1]  1  2 NA
as.character(x)
## [1] "1" "2" "a"

Por último, podemos evaluar el tipo/clase de objeto con las funciones is.numeric(), is.character(), etc.

x <- c(1,2,"a")
x
## [1] "1" "2" "a"
x <- as.numeric(x)
## Warning: NAs introducidos por coerción
is.na(x)
## [1] FALSE FALSE  TRUE

2.2.4. Tipos de objetos.

Básicamente R trabaja con los siguientes tipos de objetos:

  • VECTORES
  • MATRICES
  • DATA FRAME
  • LISTAS
  • FUNCIONES

2.2.4.1. Vectores

Lo acabamos de ver, para crear un vector se utiliza la función c() (c de concatenate). Por ejemplo:

x <- c(1,2,3,4)
x             # x es un vector que tiene cuatro componentes
## [1] 1 2 3 4
y <- c(5,6,7,8)
y
## [1] 5 6 7 8
z <- c()     # crea un vector vacío
z
## NULL

Nota: Observemos que los objetos x, y, z que habíamos definido anteriormente has sido reemplazados por los nuevos objetos.

La mayoría de las operaciones (+, -, *, /) y funciones en R están definidas con carácter vectorial. ¿Qué significa esto?

R opera componente a componente.

z <- x + y

¿Qué resultado espero obtener para z?

Exacto!!! Como la operación se realiza vectorialmente (componente a componente, muy importante!) el resultado es:

## [1]  6  8 10 12

Vamos a ver si lo entendemos de verdad. Supongamos que x e y son los siguientes vectores:

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

¿Qué longitud tienen los vectores x e y? Aquí la respuesta está clara, pero en aplicaciones reales utilizaríamos la función length().

length(x)         # esta función es muy útil, conviene recordarla.
## [1] 4
length(y)
## [1] 3

Los vectores no tienen la misma longitud, entonces… ¿cuál será el resultado de z <- x + y?

z <- x + y
## Warning in x + y: longitud de objeto mayor no es múltiplo de la longitud de uno
## menor
z
## [1] 2 4 6 5

R nos da un mensaje de aviso (warning), no es lo mismo que un error. Nos avisa que hay algo que no cuadra pero…realiza la operación que nosotros queremos.

Una cuestión muy importante que siempre tenemos que tener en cuenta cuando trabajamos con vectores es que en un vector sólo podemos concatenar elementos del mismo tipo.

Seleccionar un elemento de un vector.

Para seleccionar elementos de un objeto se suelen emplear: [], $, [[]]. Si el objeto es un vector, se utiliza []

Vamos a crear el objeto x que será un vector de cuatro componentes formado por los cuatro primeros números pares. Así:

x <- c(2,4,6,8)

Si queremos acceder/seleccionar/extraer al/el segundo componente de x

x[2]
## [1] 4

Consideremos este ejemplo:

x <- c(2,4,6,8)
y <- c(1,2,3)
z <- x + y
## Warning in x + y: longitud de objeto mayor no es múltiplo de la longitud de uno
## menor

# ¿Qué resultados darán los siguientes ejemplos?
z[6]

# si primero hacemos: z[6] <- 12
z

z[-2]
length(z)

# si primero hacemos: length(z) <- 3
z

Más tarde veremos con más profundidad cómo seleccionar elementos de un objeto. ¡Esto es esencial!

2.2.4.2. Dataframes.

Los dataframe se usan para almacenar datos en forma de tablas (filas / columnas). Este es el formato en el que estamos acostumbrados a trabajar en Excel, Spss, etc.

Los dataframe pueden almacenar objetos/datos de distinto tipo: numéricos, carácter, …

Normalmente los dataframe se crean al cargar/leer una base de datos. Por ejemplo, nosotros tenemos en el environment el objeto df, que recordemos hace referencia al precio de casas (fichero: precio_casas.xlsx). También podemos crear nosotros un dataframe utilizando la función data.frame() (lo veremos cuando escribamos cuestiones para las pruebas).

class(df)
## [1] "data.frame"

¿Cuál es la dimensión del objeto df?

nrow(df)     # número de filas
## [1] 546
ncol(df)     # número de columnas
## [1] 12
dim(df)      # número de filas y columnas
## [1] 546  12

Si echamos un vistazo al entorno global también podemos ver que df está formado por 546 observaciones de 12 variables. Si hacemos clic sobre df podemos ver el dataframe. También si escribimos:

View(df)

Una buena práctica una vez hemos cargado unos datos es ver su estructura. Esto puede hacerse haciendo clic sobre el icono azul con la flecha situado justo al lado del nombre del objeto en el entorno global o

str(df)
## 'data.frame':    546 obs. of  12 variables:
##  $ price     : num  42000 38500 49500 60500 61000 66000 66000 69000 83800 88500 ...
##  $ lotsize   : num  5850 4000 3060 6650 6360 4160 3880 4160 4800 5500 ...
##  $ bedrooms  : num  3 2 3 3 2 3 3 3 3 3 ...
##  $ bathrooms : num  1 1 1 1 1 1 2 1 1 2 ...
##  $ stories   : num  2 1 1 2 1 1 2 3 1 4 ...
##  $ driveway  : chr  "yes" "yes" "yes" "yes" ...
##  $ recreation: chr  "no" "no" "no" "yes" ...
##  $ fullbase  : chr  "yes" "no" "no" "no" ...
##  $ gasheat   : chr  "no" "no" "no" "no" ...
##  $ aircon    : chr  "no" "no" "no" "no" ...
##  $ garage    : num  1 0 0 0 0 0 2 0 0 1 ...
##  $ prefer    : chr  "no" "no" "no" "no" ...

head y tail

Normalmente los dataframes con los que trabajamos tienen muchas filas (individuos) y muchas columnas (variables). Si directamente escribimos el nombre del objeto (dataframe) para ver su contenido lo que ocurrirá es que veremos poca cosa, apenas si observaremos como R nos lista todo el contenido de forma continua.

df

Para echar un vistazo al contenido de un dataframe suelen utilizarse las funciones head() y tail(). Por defecto, la primera permite ver las 6 primeras observaciones y la segunda las 6 últimas. También podemos indicar el número de observaciones que queremos visualizar

head(df)
##   price lotsize bedrooms bathrooms stories driveway recreation fullbase gasheat
## 1 42000    5850        3         1       2      yes         no      yes      no
## 2 38500    4000        2         1       1      yes         no       no      no
## 3 49500    3060        3         1       1      yes         no       no      no
## 4 60500    6650        3         1       2      yes        yes       no      no
## 5 61000    6360        2         1       1      yes         no       no      no
## 6 66000    4160        3         1       1      yes        yes      yes      no
##   aircon garage prefer
## 1     no      1     no
## 2     no      0     no
## 3     no      0     no
## 4     no      0     no
## 5     no      0     no
## 6    yes      0     no
tail(df)
##      price lotsize bedrooms bathrooms stories driveway recreation fullbase
## 541  85000    6525        3         2       4      yes         no       no
## 542  91500    4800        3         2       4      yes        yes       no
## 543  94000    6000        3         2       4      yes         no       no
## 544 103000    6000        3         2       4      yes        yes       no
## 545 105000    6000        3         2       2      yes        yes       no
## 546 105000    6000        3         1       2      yes         no       no
##     gasheat aircon garage prefer
## 541      no     no      1     no
## 542      no    yes      0     no
## 543      no    yes      0     no
## 544      no    yes      1     no
## 545      no    yes      1     no
## 546      no    yes      1     no
head(df,10)
tail(df,10)

Seleccionar elementos de un dataframe

Para seleccionar elementos de un dataframe utilizamos los símbolos $ o []. La forma de proceder es similar a la que se ha visto con vectores o matrices.

Si queremos seleccionar la variable bedrooms del objeto df:

df$bedrooms     # observad que al comenzar la escritura RStudio facilita una ayuda

# También lo podemos hacer así:
df[3]

y para seleccionar sus cinco primeros elementos:

df$bedrooms[1:5]     # los : es una forma de obtener una secuencia, más adelante veremos otras formas
## [1] 3 2 3 3 2
df[1:5,1]
## [1] 42000 38500 49500 60500 61000

Podemos incluir directamente una nueva variable a nuestro dataframe. Por ejemplo, vamos a añadir la variable id (de identificador) al objeto df. Esto lo podemos hacer directamente utilizando el símbolo $.

df$id <- 1:nrow(df)  # los : se utilizan para generar una secuencia
# en el apartado 3 volveremos a referirnos a las secuencias

df

o podemos crear la nueva variable, por ejemplo la variable obs (de observación) y después combinarla con nuestro dataframe df.

obs <- 1:nrow(df)
df <- cbind(obs,df) # la función cbind se utiliza para juntar. También puede 
# utilizarse la función rbind
df

Ahora vamos a practicar la selección de datos (observaciones y/o variables) en un dataframe (funciona igual si trabajamos con matrices).

En primer lugar, seleccionamos las variables: price, lotsize, bedrooms y bathrooms.

df2 <- df[,1:4]

Ahora, seleccionamos del objeto df datos las variables: **price, lotsize* y aircon.

df3 <- df[,c(1,2,10)]  # también df3<- df[c(1,2,10)]

Sin en lugar de seleccionar variables (columnas) estamos interesados en seleccionar individuos/observaciones (filas):

df4 <- df[1:6,]
# mirar la ayuda de la función seq() y seleccionar las observaciones 4,8,12,...,28,32.
df5 <- df[seq(0,nrow(df),4),]

Para seleccionar tanto observaciones como variables no tenemos más que combinar las estrategias anteriores:

df6 <- df[seq(0,nrow(df),4), c(2,3,7)] 

En ocasiones estamos interesados en seleccionar los casos para los que cierta variable toma determinado valor. Vamos a ver primero un resumen de los datos que tenemos:

summary(df)
##      price           lotsize         bedrooms       bathrooms    
##  Min.   : 25000   Min.   : 1650   Min.   :1.000   Min.   :1.000  
##  1st Qu.: 49125   1st Qu.: 3600   1st Qu.:2.000   1st Qu.:1.000  
##  Median : 62000   Median : 4600   Median :3.000   Median :1.000  
##  Mean   : 68122   Mean   : 5150   Mean   :2.965   Mean   :1.286  
##  3rd Qu.: 82000   3rd Qu.: 6360   3rd Qu.:3.000   3rd Qu.:2.000  
##  Max.   :190000   Max.   :16200   Max.   :6.000   Max.   :4.000  
##     stories        driveway          recreation          fullbase        
##  Min.   :1.000   Length:546         Length:546         Length:546        
##  1st Qu.:1.000   Class :character   Class :character   Class :character  
##  Median :2.000   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :1.808                                                           
##  3rd Qu.:2.000                                                           
##  Max.   :4.000                                                           
##    gasheat             aircon              garage          prefer         
##  Length:546         Length:546         Min.   :0.0000   Length:546        
##  Class :character   Class :character   1st Qu.:0.0000   Class :character  
##  Mode  :character   Mode  :character   Median :0.0000   Mode  :character  
##                                        Mean   :0.6923                     
##                                        3rd Qu.:1.0000                     
##                                        Max.   :3.0000

Ahora, seleccionamos price y bedrooms para todas las observaciones en las que la variable bathrooms satisfaga un valor:


unique(df$bathrooms)    # valores que toma la variable bathrooms
## [1] 1 2 3 4
sort(unique(df$bathrooms))   # observad la diferencia con order()
## [1] 1 2 3 4

df7 <- df[df$bathrooms==2, c(1,3)]   # observad como al establecer la igualdad se utiliza ==

df8 <- df[df$bathrooms>=2 & df$bathrooms<48, c(1,3)] 

Para seleccionar subconjuntos de datos en un dataframe también podemos utilizar la función subset().

df9 <- subset(df, bathrooms==2 & lotsize <= mean(lotsize), select=c(price,bedrooms))

df10 <- subset(df, bathrooms !=2 & lotsize <= mean(lotsize))    # el símbolo ! equivale a "lo contrario"

Nombres de filas/columnas

En los dataframe las columnas representarían variables y las filas representarían individuos (observaciones).

Si las columnas de un data frame no tienen nombres , podemos incluirlos utilizando la función names(). Para incluir nombres a las filas se utiliza la función row.names()

names(df)
##  [1] "price"      "lotsize"    "bedrooms"   "bathrooms"  "stories"   
##  [6] "driveway"   "recreation" "fullbase"   "gasheat"    "aircon"    
## [11] "garage"     "prefer"

# ¿Qué hace esto?
df$id <- row.names(df) 
row.names(df) <- 1:nrow(df)     # observad los cambios

2.2.4.3. Listas.

Las listas pueden contener elementos/componentes de distinto tipo.


Observemos esta lista que tiene 5 componentes (pueden ser matrices, vectores, dataframes,..).

x <- list(c(1,2,3,4), "Curso", F, 1+2i, 3L)
x
## [[1]]
## [1] 1 2 3 4
## 
## [[2]]
## [1] "Curso"
## 
## [[3]]
## [1] FALSE
## 
## [[4]]
## [1] 1+2i
## 
## [[5]]
## [1] 3

Utilizamos el doble corchete [[]] para acceder al contenido concreto de una lista.

x[[3]]  # accedemos al tercer componente de la lista
## [1] FALSE
x[[1]][2] # accedemos al segundo elemento del primer componente de la lista
## [1] 2

Vamos a crear otra lista para practicar.

y <- list( Titulacion = c("Economía", "Sociología", "Derecho"), Edad =c(25,26,27))

y
## $Titulacion
## [1] "Economía"   "Sociología" "Derecho"   
## 
## $Edad
## [1] 25 26 27

Fijémonos en la diferencia de presentación de las listas x e y. Como en la lista y hemos nombrado los componentes, estos aparecen al ejecutar el objeto precedidos del símbolo $. Ahora también podemos acceder a un componente de la lista por su nombre.

y$Titulacion
## [1] "Economía"   "Sociología" "Derecho"
y[[1]]
## [1] "Economía"   "Sociología" "Derecho"
y[1]
## $Titulacion
## [1] "Economía"   "Sociología" "Derecho"
y[[1]][1]
## [1] "Economía"
y$Titulacion[1]
## [1] "Economía"

Evidentemente, también podemos realizar operaciones con listas.

y[[2]]*3
## [1] 75 78 81

Podemos crear una lista vacía con una determinada longitud:

z <- vector("list", length= 3)

2.2.5. Breve referencia a Factores.

Los factores, que pueden ser ordenados o no ordenados, se utilizan para representar variables de naturaleza categórica. Generalmente es preferible trabajar con carácteres y suele dejarse los factores cuando se necesitan en un análisis estadístico, por ejemplo en el análisis de regresión.

factor_nominal <- factor(rep(c("Ford","Seat","Renault"),10))
levels(factor_nominal)     # ordena los factores por orden alfabético
## [1] "Ford"    "Renault" "Seat"

nuevo_factor_nominal <- factor(factor_nominal, levels=c("Seat","Renault","Ford"))  # reordenación de factores
levels(nuevo_factor_nominal)
## [1] "Seat"    "Renault" "Ford"

Vamos a cargar la base de datos iris, que se encuentra en el paquete datasets(). Iris contiene información sobre longitud y anchura de pétalos y sépalos y especie de un total de 150 lirios.

data("iris")
str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

Como podemos ver, el tipo de especie (Species) es una variable categórica (o factor) que tiene tres niveles (levels): setosa, versicolor, virginica. Vamos a ver la distribución del tipo de especie con una tabla

levels(iris$Species)
## [1] "setosa"     "versicolor" "virginica"
table(iris$Species)
## 
##     setosa versicolor  virginica 
##         50         50         50

En ocasiones, cuando cargamos variables que son carácter se crean como factores.

Si vamos a realizar un análisis de regresión es conveniente guardar las variables categóricas como factores (R codificará internamente los distintos niveles del factor como enteros). Además, puede que sea de nuestro interés cambiar el orden de los niveles. Para aprender más sobre factores aquí.

3. SECUENCIAS Y REPETICIONES

Para crear secuencias y repeticiones se utilizan los : y las funciones seq(), rep(). Veamos estas funciones a través de varios ejemplos de aplicación.

seq(from = -3, to = 3, by = 0.1)
##  [1] -3.0 -2.9 -2.8 -2.7 -2.6 -2.5 -2.4 -2.3 -2.2 -2.1 -2.0 -1.9 -1.8 -1.7 -1.6
## [16] -1.5 -1.4 -1.3 -1.2 -1.1 -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1
## [31]  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1.0  1.1  1.2  1.3  1.4
## [46]  1.5  1.6  1.7  1.8  1.9  2.0  2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9
## [61]  3.0
seq(-3, 3, 0.1)
##  [1] -3.0 -2.9 -2.8 -2.7 -2.6 -2.5 -2.4 -2.3 -2.2 -2.1 -2.0 -1.9 -1.8 -1.7 -1.6
## [16] -1.5 -1.4 -1.3 -1.2 -1.1 -1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1
## [31]  0.0  0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1.0  1.1  1.2  1.3  1.4
## [46]  1.5  1.6  1.7  1.8  1.9  2.0  2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9
## [61]  3.0
seq(-3, 3, length = 50)
##  [1] -3.00000000 -2.87755102 -2.75510204 -2.63265306 -2.51020408 -2.38775510
##  [7] -2.26530612 -2.14285714 -2.02040816 -1.89795918 -1.77551020 -1.65306122
## [13] -1.53061224 -1.40816327 -1.28571429 -1.16326531 -1.04081633 -0.91836735
## [19] -0.79591837 -0.67346939 -0.55102041 -0.42857143 -0.30612245 -0.18367347
## [25] -0.06122449  0.06122449  0.18367347  0.30612245  0.42857143  0.55102041
## [31]  0.67346939  0.79591837  0.91836735  1.04081633  1.16326531  1.28571429
## [37]  1.40816327  1.53061224  1.65306122  1.77551020  1.89795918  2.02040816
## [43]  2.14285714  2.26530612  2.38775510  2.51020408  2.63265306  2.75510204
## [49]  2.87755102  3.00000000
rep(1, times = 5)
## [1] 1 1 1 1 1
rep(1, 5)
## [1] 1 1 1 1 1
rep(c(1,2), 5)
##  [1] 1 2 1 2 1 2 1 2 1 2
1:10
##  [1]  1  2  3  4  5  6  7  8  9 10
rep(1:10,5)
##  [1]  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5
## [26]  6  7  8  9 10  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 10

4. FUNCIÓN apply()

Las funciones de la familia apply son bastante utilizadas por su sencillez y “potencia”. En general, utilizan una matriz, lista o dataframe como input sobre la que se aplica una función. En muchas situaciones permite evitar crear loops cuando se escriben funciones. La familia apply está compuesta por varias funciones (apply, lapply, sapply, tapply, mapply, vapply, rapply), pero las más comunes es la función apply.

Para ver los argumentos de la función apply() vamos a leer la ayuda de R.

help(apply)

Como vemos, apply() se utiliza para pasar una función (por ejemplo, si queremos calcular la media la función mean()) sobre una matriza, array, lista o dataframe (argumento X) bien por filas (MARGIN=1) o columnas (MARGIN=2).

Esquemáticamente:

Fuente: DataCamp

Fuente: DataCamp


Por ejemplo, vamos a calcular la media de las 5 primeras columnas de df.

apply(df[,1:5], 2, mean)

y ahora un resumen:

apply(df[,1:5], 2, summary)

5. CASO PRÁCTICO

El objetivo que se persigue con el desarrollo de este caso es practicar lo visto hasta el momento e introducir el uso de funciones básicas de estadística para ver en contexto cómo utilizarlas.

Descargad dentro de la carpeta datos el fichero de Excel que se encuentra en: https://www.uv.es/vcoll/curso_R_sfp/precio_casas.xlsx

Como podéis ver, son los datos de los precios de las casas que trabajamos anteriormente.

Cargad los datos y asignarlos al objeto datos.

library(tidyverse)  # cargo esta librería porque más adelante comparamos algunas instrucciones de RBase con tidyverse.
library(rio) # si no se ha cargado en la sesión de trabajo

# Cargamos los datos. obervar los símbolos de asignación. 
datos <- import("datos/precio_casas.xlsx") # el más habitual
datos2 = import("datos/precio_casas.xlsx")
import("datos/precio_casas.xlsx") -> datos3

rm(datos3) # elimino el objeto datos3

# ver los datos
View(datos)

# ver estructura de los datos
str(datos)
glimpse(datos) #tidyverse

# resumen de los datos
summary(datos)

# cambiar 1 variable character a factor
datos$prefer <- as.factor(datos$prefer)
summary(datos)

# cambiar todas las variables caracter a factor
datos[sapply(datos, is.character)] <- lapply(datos[sapply(datos, is.character)], 
                                       as.factor)
rm(datos)

datos <- datos2 %>% mutate_if(is.character,as.factor) # MÁS FÁCIL CON DPLYR!!! 
# En la próxima sesión se explicará con más detenimiento.  
summary(datos)

# tablas de frecuencias
freq_bed <- table(datos$bedrooms) # es una table
freq_bed2 <- datos %>% group_by(bedrooms) %>% count() # tb con tally

freq_cum_bed <- cumsum(freq_bed)
freq_cum_bed2 <- datos %>% 
  count(bedrooms) %>%
  mutate(freq_cum = cumsum(n))


freq_rel_bed <- prop.table(freq_bed)
freq_cum_rel_bed <- cumsum(freq_rel_bed)

freq_rel_bed2 <- freq_cum_bed2 %>% mutate(freq_rel_bed2 = n / sum(n),
                                          freq_cum_rel_bed2 = freq_cum / sum(n))

# tablas de valores agrupados
precio <- datos$price
precio2 <- cut(precio, seq(from = min(precio), 
                           to = max(precio), 
                           by = (max(precio) - min(precio))/20), 
                           include.lowest=TRUE)
table(precio2) %>% as.data.frame()

# tabla cruzada

table(datos$bedrooms, datos$bathrooms)

library(descr) # En esta introducción, solo por curiosidad: crea tablas parecidas a las de SPSS
crosstab(datos$bedrooms, datos$bathrooms, prop.c = TRUE, plot = FALSE)

# MEDIDAS DE POSICION
mean(datos$bedrooms, na.rm = TRUE)
weighted.mean(freq_bed2$bedrooms,freq_bed2$n)

median(datos$bedrooms, na.rm = TRUE)
median(datos$bedrooms, na.rm = TRUE, type=5) 
# type= 5 es más parecido a lo que suele encontrar en manuales

quantile(datos$bedrooms, c(.25,.5,.75), na.rm = TRUE, type = 5)

#install.packages("reldist")
library(reldist)
wtd.quantile(freq_bed2$bedrooms, 0.5, weight=freq_bed2$n, na.rm = TRUE)
wtd.quantile(freq_bed2$bedrooms, c(.25,.5,.75), weight=freq_bed2$n, na.rm = TRUE)

# cálculo de la moda: creo una función que detecta modas y omite los NAs
moda <- function(x) { 
  distintos_valores <- na.omit(unique(x)) # valores distintos
  veces_x <- tabulate(match(x, distintos_valores)) # veces que se repite cada valor
  distintos_valores[veces_x == max(veces_x)] # identifica los valores que mas se repiten
}

moda(datos$bedrooms)

# esto también permite identificar varias modas
moda2 <- table(datos$bedrooms)
names(moda2)[which(moda2==max(moda2))]

# instalar algún paquete que tenga la función built-in. Por ejemplo:
# los paquetes modeest -función mfv()- y DescTools -función Mode()-

#if (!require("remotes")) install.packages("remotes")
#remotes::install_github("AndriSignorell/DescTools")

# y si los datos se presentan en una tabla de frecuencias, ¿cómo calcular la moda?
datos2 <- data.frame("x" = c(1,2,3,1,2),
                     "n" = c(3,2,1,5,4))

datos2 %>% group_by(x) %>% 
  summarize(freq = sum(n)) %>% 
  arrange(desc(freq)) %>%
  filter(freq == max(freq))
  

# MEDIDAS DE DISPERSION
datos %>% summarize(bed = n(),
                    varianza_muestral = var(datos$bedrooms),
                    desv_tip_muestral = sd(datos$bedrooms),
                    varianza_poblacional = ((bed-1)/bed)*varianza_muestral,
                    rango = max(bedrooms) - min(bedrooms),
                    IQR = IQR(bedrooms),
                    coef_var = desv_tip_muestral/mean(datos$bedrooms))

# medidas de forma

#install.packages("moments")
library(moments)
moments::skewness(datos$bedrooms)
moments::kurtosis(datos$bedrooms)

#install.packages("e1071")
library(e1071) 
e1071::skewness(datos$bedrooms)
e1071::kurtosis(datos$bedrooms)

# covarianza (muestral) y correlación
cov(datos$bedrooms,datos$price)

cor(datos$bedrooms,datos$price)

# covarianza (poblacional)

((nrow(datos)-1) / nrow(datos)) * cov(datos$bedrooms,datos$price)

# matrices de var-covar (muestral) y correlación
var(datos[c(1,3,4)], na.rm = TRUE) # también: var(datos[,c(1,3,4)])
cov(datos[c(1,3,4)])
cor(datos[c(1,3,4)])

# matrices de var-cov (poblacional)
((nrow(datos)-1) / nrow(datos)) * var(datos[c(1,3,4)], na.rm = TRUE)
((nrow(datos)-1) / nrow(datos)) * cov(datos[c(1,3,4)])

# regresión lineal

modelo <- lm(price ~ bedrooms, data= datos)
summary(modelo)

modelo$coefficients
modelo$fitted.values  # fitted.values(modelo)
modelo$residuals      # residuals(modelo)

str(summary(modelo))

summary(modelo)$r.squared

6. Referencias útiles


Para facilitarnos la vida con latex: