Introducción a R base: de la celda a la tabla
Cuadernos prácticos de Software para la gestión de bases de datos del Máster de Bioestadística (curso 2024-2025)
1 Introducción a R (base)
1.1 Requisitos iniciales
1.1.1 R
Si es la primera vez que usas R
(¡bienvenido/a!) antes vas a necesitar, con conexión a internet (también lo necesitarás para la descarga de algunos datos y paquetes), instalar R
Como sucede cuando escribimos un idioma (castellano, por ejemplo), R
será nuestro lenguaje y nuestra gramática, en definitiva, nuestras reglas de juego.
Para su instalación necesitarás seguir los siguientes pasos:
Paso 1: entra en https://cran.r-project.org/ y selecciona tu sistema operativo.
Paso 2: para Mac basta con que hacer click en el archivo .pkg, y abrirlo una vez descargado. Para sistemas Windows, debemos clickar en install R for the first time y después en Download R for Windows. Una vez descargado, abrirlo como cualquier archivo de instalación.
Paso 3: abrir el ejecutable de instalación.
Siempre que tengas que descargar algo de CRAN (ya sea el propio R o un paquete), asegúrate de tener conexión a internet.
Para comprobar que se ha instalado correctamente, tras abrir R
, deberías ver el R GUI
(Graphical User Interface), y en él, una pantalla blanca similar a esta: se llama consola y podemos hacer un primer uso de ella como una calculadora.
Para empezar vamos a generar nuestro primer código: a una variable llamada a
le asignaremos el valor 1
(escribiremos el código de abajo en la consola y daremos «enter»)
Tras ello definiremos otra variable llamada b
y le asignaremos el valor 2
Tras definir ambas variables haremos la suma a + b
y nos devolverá su resultado
En R
asignaremos valores con <-
como una flecha: la variable a la izquierda de dicha flecha le asignamos el valor que hay a la derecha (por ejemplo, a <- 1
)
En la consola aparece un número [1]
: simplemente es un contador de elementos (como contar filas en un Word)
1.1.2 RStudio
No es obligatorio pero si recomendable trabajar en un IDE (entorno de desarrollo), algo parecido a un Word para escribir castellano, y ese será el RStudio
. Para instalar RStudio debes seguir los siguientes pasos
Paso 1: entra la web oficial de RStudio (ahora llamado Posit) y selecciona la descarga gratuita.
Paso 2: selecciona el ejecutable que te aparezca acorde a tu sistema operativo.
Paso 3: tras descargar el ejecutable, hay que abrirlo como otro cualquier otro y dejar que termine la instalación.
Al abrir RStudio
seguramente tengas tres ventanas:
- Consola: es el nombre para llamar a la ventana grande que te ocupa buena parte de tu pantalla. Prueba a escribir el mismo código que antes (la suma de las variables) en ella. La consola será donde ejecutaremos órdenes y mostraremos resultados.
- Environment: la pantalla pequeña (puedes ajustar los márgenes con el ratón a tu gusto) que tenemos en la parte superior derecha. Nos mostrará las variables que tenemos definidas.
- Panel multiusos: la ventana que tenemos en la parte inferior derecha no servirá para buscar ayuda de funciones, además de para visualizar gráficos.
1.2 Idea fundamental: paquetes
Una de las ideas claves de R
es el uso de paquetes: códigos que otras personas han implementado para resolver un problema
Con los paquetes podemos hacer dos cosas: instalarlos o usarlos.
- Instalación: descargamos los códigos de la web (necesitamos internet) → comprar un libro, solo una vez (por ordenador)
install.packages("ggplot2")
- Carga: con el paquete descargado, indicamos qué paquetes queremos usar cada vez que abramos
RStudio
→ traer el libro de la estantería (usando el nombre del paquete sin comillas)
library(ggplot2)
También podemos hacer uso solo de funciones concretas: usando paquete::funcion
le índicamos que solo queremos una página concreta de ese libro
::geom_point() ggplot2
1.3 Mensajes de error
Durante tu aprendizaje va a ser muy habitual que las cosas no salgan a la primera → te vas equivocar. No solo será importante asumirlo sino que es importante leer los mensajes de error para aprender de ellos.
- Mensajes de error: precedidos de «Error in…» y serán aquellos fallos que impidan la ejecución
"a" + 1
Error in "a" + 1: non-numeric argument to binary operator
- Mensajes de warning: precedidos de «Warning in…» son los (posibles) fallos más delicados ya que son incoherencias que no impiden la ejecución
# Ejecuta la orden pero el resultado es NaN, **Not A Number**, un valor que no existe
sqrt(-1)
Warning in sqrt(-1): NaNs produced
[1] NaN
1.4 Scripts y proyectos
Por último, vamos a mencionar el concepto de script: el documento en el que programamos, nuestro archivo .doc
(aquí con extensión .R
) donde escribiremos las órdenes. Para abrir nuestro primero script, haz click en el menú en File < New File < R Script
.
Es importante no abusar de la consola: todo lo que no escribas en un script, cuando cierres, lo habrás perdido.
R
es case-sensitive: es sensible a mayúsculas y minúsculas por lo que x
y X
representa variables distintas.
Una vez creado el script tenemos una cuarta ventana en RStudio
: la ventana donde escribiremos nuestros códigos. ¿Cómo ejecutarlo?
Escribimos el código a ejecutar.
Guardamos el archivo .R haciendo click en
Save current document
.El código no se ejecuta salvo que se lo indiquemos. Tenemos tres opciones de ejecutar un script:
- Copiar y pegar en consola.
- Seleccionar líneas y
Ctrl+Enter
- Activar Source on save a la derecha de guardar: no solo guarda sino que ejecuta el código completo.
1.4.1 Proyectos
De la misma manera que en el ordenador solemos trabajar de manera ordenada por carpetas, en RStudio
podemos hacer lo mismo para trabajar de manera eficaz creando proyectos.
Un proyecto será una «carpeta» dentro de RStudio
, de manera que nuestro directorio raíz automáticamente será la propia carpeta de proyecto (pudiendo pasar de un proyecto a otro con el menu superior derecho). Podemos crear uno en una carpeta nueva o en una carpeta ya existente.
1.5 💻 Ejercicios
📝 Crea en tu ordenador una carpeta de la asignatura y crea dentro de ella el proyecto de RStudio
: es ahí donde vas a guardar todo lo que hagamos a lo largo de este curso.Tras crear el proyecto tendrás un archivo R Project
. A continuación crea en dicha carpeta dos subcarpetas: datos
(es ahí donde irás guardando los distintos datasets que usaremos) y scripts
(es ahí donde irás guardando los archivos .R
de cada clase)
📝 Dentro del proyecto crea un script ejercicios-clase1.R
(dentro de la carpeta scripts
). Una vez creado define en él una variable de nombre a
y cuyo valor sea -1. Ejecuta el código de las 3 maneras explicadas.
Code
<- -1 a
📝 Añade debajo otra línea para definir una variable b
con el valor 5. Tras ello guarda la multiplicación de ambas variables. Ejecuta el código como consideres.
Code
<- 5
b * b # sin guardar
a <- a * b # guardado multiplicacion
📝 Modifica el código inferior para definir dos variables c y d, con valores 3 y -1. Tras ello divide las variables y guarda el resultado.
<- # deberías asignarle el valor 3
c <- # deberías asignarle el valor -1 d
Code
<- 3
c <- -1
d / d # sin guardar
c <- c / d # guardado division
📝 Asigna un valor positivo a x
y calcula su raíz cuadrada; asigna otro negativo y
y calcula su valor absoluto con la función abs()
.
Code
<- 5
x sqrt(x)
<- -2
y abs(y)
Comandos como sqrt()
, abs()
o max()
son lo que llamamos funciones: líneas de código que hemos «encapsulado» bajo un nombre, y dado unos argumentos de entrada, ejecuta las órdenes (una especie de atajo). En las funciones los argumentos irán SIEMPRE entre paréntesis
📝 Usando la variable x
ya definida, completa/modifica el código inferior para guardar en una nueva variable z
el resultado guardado en x
menos 5.
<- ? - ? # completa el código
z z
Code
<- x - 5
z z
📝 Define una variable x
y asígnale el valor -1
. Define otra y
y asígnale el valor 0
. Tras ello realiza las operaciones a) x
entre y
; b) raíz cuadrada de x
. ¿Qué obtienes?
Code
<- -1
x <- 0
y
/ y
x sqrt(x)
📝 Escribe el código inferior en tu script. ¿Por qué crees que no funciona?
<- -1
x <- 0
y
+ y X
Error in eval(expr, envir, enclos): object 'X' not found
2 Primeros tipos de datos
Dado que es un curso de R
orientado a ciencia de datos (normalmente con datos tabulados en tablas), la primera pregunta que quizás nos hagamos es
Dada una tabla, ¿qué tipo de dato podemos tener en cada celda?
La idea del curso es ir de lo concreto (celda individual) a lo general (tablas y listas)
- Celda: dato individual de un tipo concreto.
- Variable: concatenación de valores del mismo tipo (vectores en R).
- Matriz: concatenación de variables del mismo tipo y longitud.
- Tabla: concatenación de variables de distinto tipo pero igual longitud
- Lista: concatenación de variables de distinto tipo y distinta longitud :::
2.1 Tipos de datos individuales
Y aunque la mayoría de veces tendemos a pensar que solo guardamos y trabajamos con números, ¿existen variables más allá de los números en la ciencia de datos?
Piensa por ejemplo en los datos que podrías guardar de una persona:
- La edad o el peso será un número.
<- 33 edad
- Su nombre será una cadena de texto (conocida como string o char).
<- "javi" nombre
- A la pregunta «¿estás matriculado en la Facultad?» la respuesta será lo que llamamos una variable lógica (
TRUE
si está matriculado oFALSE
en otro caso).
<- TRUE matriculado
- Su fecha de nacimiento será precisamente eso, una fecha (veremos como definirlas un poco más adelante).
2.1.1 Variables numéricas
El dato más sencillo (ya lo hemos usado) serán las variables numéricas
<- 5
a <- 2
b + b a
[1] 7
Para saber la clase de dato en R de una variable tenemos la función class()
class(a)
[1] "numeric"
Para saber su tipología (naturaleza o formato) variable tenemos typeof()
typeof(1) # 1 pero almacenado como un valor real (double, con decimales)
[1] "double"
typeof(as.integer(1)) # 1 pero almacenado como un entero.
[1] "integer"
En R
tenemos una colección de funciones que empiezan por as.x()
y que sirven como funciones de conversión: un dato que era de un tipo, lo convertimos a tipo x
.
Además de los números «normales» tendremos el valor más/menos infinito codificado como Inf
o -Inf
1/0
[1] Inf
-1/0
[1] -Inf
Y también aparecerán a menudo valores que no son números reales, definidos como not a number (indeterminaciones, complejos, etc), y codificados como NaN
0/0
[1] NaN
sqrt(-2)
Warning in sqrt(-2): NaNs produced
[1] NaN
Con las variables numéricas podemos realizar las operaciones aritméticas de una calculadora: sumar (+
)…
+ b a
[1] 7
…raíz cuadrada (sqrt()
)…
sqrt(a)
[1] 2.236068
… potencias (^2
, ^3
)…
^2 a
[1] 25
…valor absoluto (abs()
), etc.
abs(a)
[1] 5
2.1.2 Variables de texto
Imagina que además de la edad de una persona queremos guardar su nombre: ahora la variable será de tipo character
<- "Javier"
nombre class(nombre)
[1] "character"
Las cadenas de texto son un tipo con el que obviamente no podremos hacer operaciones aritméticas (sí otras operaciones como pegar o localizar patrones).
+ 1 # error al sumar número a texto nombre
Error in nombre + 1: non-numeric argument to binary operator
Las variables de tipo texto (character o string) van SIEMPRE entre comillas: no es lo mismo TRUE
(valor lógico, binario) que "TRUE"
(texto).
Y aunque no podamos aplicar a las cadenas de texto operaciones aritméticas habituales si podremos aplicar otras operaciones, como por ejemplo, la función paste()
. Como hemos comentado R
llamaremos función a un trozo de código encapsulado bajo un nombre, y que depende de unos argumentos de entrada.
Nuestra primera función paste()
toma dos cadenas de texto y nos permite pegarlas.
paste("Javier", "Álvarez")
[1] "Javier Álvarez"
Fíjate que por defecto nos pega las cadenas con un espacio, pero podemos añadir un argumento opcional para indicarle el separador (en sep = ...
).
paste("Javier", "Álvarez", sep = "*")
[1] "Javier*Álvarez"
¿Cómo saber qué argumentos necesita una función? Escribiendo en consola ? paste
te aparecerá una ayuda en el panel multiusos.
En dicha ayuda podrás ver en su cabecera que argumentos ya tiene asignados por defecto la función. Los argumentos (y su detalle) también pueden ser consultado tabulando (detrás de una coma).
Existe una función similar llamada paste0()
que pega por defecto con sep = ""
(sin nada).
paste0("Javier", "Álvarez")
[1] "JavierÁlvarez"
Es muy importante entender el concepto de argumento por defecto de una función en R
: es un valor que la función usa pero a veces podemos no ver porque ya tiene un valor asignado.
# Hacen lo mismo
paste("Javier", "Álvarez")
[1] "Javier Álvarez"
paste("Javier", "Álvarez", sep = " ")
[1] "Javier Álvarez"
El operador =
lo reservaremos para asignar argumentos dentro de funciones. Para todas las demás asignaciones usaremos <-
Otra forma de concatenar cadenas de texto (más intuitiva) es usar el paquete {glue}
: lo primero que haremos será «comprar el libro» (si nunca lo hemos hecho). Tras ello cargamos el paquete
install.packages("glue") # solo la primra vez
library(glue)
Con la función glue()
de dicho paquete podemos usar variables dentro de cadenas de texto. Por ejemplo, «la edad es de … años», donde la edad está guardada en una variable.
<- 33
edad glue("La edad es de {edad} años")
La edad es de 33 años
Dentro de las llaves también podemos ejecutar operaciones
<- "días"
unidades glue("La edad es de {edad * 365} {unidades}")
La edad es de 12045 días
2.1.3 Variables lógicas
Otro tipo fundamental serán las variables lógicas o binarias (dos valores):
TRUE
: verdadero guardado internamente como un 1.FALSE
: falso guardado internamente como un 0.
<- TRUE # ¿Es soltero? --> SÍ
soltero class(soltero)
[1] "logical"
Dado que internamente están guardados como variables binarias, podemos realizar operaciones aritméticas con ellas
2 * TRUE
[1] 2
FALSE - 1
[1] -1
Como veremos en breve, las variables lógicas en realidad puede tomar un tercer valor: NA
o dato ausente, representando las siglas de not available, y será muy habitual encontrarlo dentro de una base de datos.
<- NA
ausente + 1 ausente
[1] NA
Las variables lógicas NO son variables de texto: "TRUE"
es un texto, TRUE
es un valor lógico.
TRUE + 1
[1] 2
"TRUE" + 1
Error in "TRUE" + 1: non-numeric argument to binary operator
Los valores lógicos suelen ser resultado de evaluar condiciones lógicas. Por ejemplo, imaginemos que queremos comprobar si una persona se llama Javi.
<- "María" nombre
Con el operador lógico ==
preguntamos sí lo que tenemos guardado a la izquierda es igual que lo que tenemos a la derecha: es una pregunta
== "Javi" nombre
[1] FALSE
Con su opuesto !=
preguntamos si es distinto.
!= "Javi" nombre
[1] TRUE
No es lo mismo <-
(asignación) que ==
(estamos preguntando, es una comparación lógica).
Además de las comparaciones «igual a» frente «distinto», también comparaciones de orden como menor que <
, mayor que >
, <=
o >=
.
¿Tiene la persona menos de 32 años?
<- 34
edad < 32 # ¿Es la edad menor de 32 años? edad
[1] FALSE
¿La edad es mayor o igual que 38 años?
>= 38 edad
[1] FALSE
¿El nombre guardado es Javi?
<- "Javi"
nombre == "Javi" nombre
[1] TRUE
2.1.4 Variables de fecha
Un tipo de datos muy especial: los datos de tipo fecha.
<- "2021-04-21" fecha_char
Parece una simple cadena de texto pero debería representar un instante en el tiempo. ¿Qué debería suceder si sumamos un 1 a una fecha?
+ 1 fecha_char
Error in fecha_char + 1: non-numeric argument to binary operator
Las fechas NO pueden ser texto: debemos convertir la cadena de texto a fecha.
Para trabajar con fechas usaremos el paquete {lubridate}
, que deberemos instalar antes de poder usarlo.
install.packages("lubridate")
Una vez instalado, de todos los paquetes (libros) que tenemos, le indicaremos que nos cargue ese concretamente.
library(lubridate) # instala si no lo has hecho
Attaching package: 'lubridate'
The following objects are masked from 'package:base':
date, intersect, setdiff, union
Para convertir a tipo fecha usaremos la función as_date()
del paquete {lubridate}
(por defecto en formato yyyy-mm-dd
)
# ¡no es una fecha, es un texto!
+ 1 fecha_char
Error in fecha_char + 1: non-numeric argument to binary operator
class(fecha_char)
[1] "character"
<- as_date("2023-03-28") # si es una fecha
fecha + 1 fecha
[1] "2023-03-29"
class(fecha)
[1] "Date"
En as_date()
el formato de fecha por defecto es yyyy-mm-dd
así que si la cadena de texto no se introduce de manera adecuada…
as_date("28-03-2023")
Warning: All formats failed to parse. No formats found.
[1] NA
… devuelve un ausnete.
Para cualquier otro formato debemos especificarlo en el argumento opcional format = ...
tal que %d
representa días, %m
meses, %Y
en formato de 4 años y %y
en formato de 2 años.
as_date("28-03-2023", format = "%d-%m-%Y")
[1] "2023-03-28"
as_date("28-03-23", format = "%d-%m-%y")
[1] "2023-03-28"
as_date("03-28-2023", format = "%m-%d-%Y")
[1] "2023-03-28"
as_date("28/03/2023", format = "%d/%m/%Y")
[1] "2023-03-28"
En dicho paquete tenemos además funciones muy útiles para manejar fechas:
- Con
today()
podemos obtener directamente la fecha actual.
today()
[1] "2024-09-19"
- Con
now()
podemos obtener la fecha y hora actual
now()
[1] "2024-09-19 14:14:42 CEST"
- Con
year()
,month()
oday()
podemos extraer el año, mes y día
<- today()
fecha year(fecha)
[1] 2024
month(fecha)
[1] 9
Tienes un resumen en pdf de los paquetes más importantes en la carpeta correspondiente en el campus
2.2 Vectores: concatenar
Cuando trabajamos con datos normalmente tendremos columnas que representan variables: llamaremos vectores a una concatenación de celdas (valores) del mismo tipo (lo que sería una columna de una tabla).
La forma más sencilla es con el comando c()
(c de concatenar), y basta con introducir sus elementos entre paréntesis y separados por comas
<- c(32, 27, 60, 61)
edades edades
[1] 32 27 60 61
Un número individual x <- 1
(o bien x <- c(1)
) es en realidad un vector de longitud uno –> todo lo que sepamos hacer con un número podemos hacerlo con un vector de ellos.
Como ves ahora en el environment
tenemos una colección de elementos guardada
edades
[1] 32 27 60 61
De aquí en adelante calcularemos la longitud de un vector con la función length()
length(edades)
[1] 4
También podemos concatenar vectores entre sí (los repite uno tras otro)
c(edades, edades, 8)
[1] 32 27 60 61 32 27 60 61 8
2.2.1 Secuencias numéricas
El vector más famoso será el de tipo numérico, y en concreto, las conocidas como secuencias numéricas (por ejemplo, los días del mes), usadas para, entre otras cosas, indexar bucles.
El comando seq(inicio, fin)
nos permite crear una secuencia numérica desde un elemento inicial hasta uno final, avanzando de uno en uno.
seq(1, 31)
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[26] 26 27 28 29 30 31
Fíjate que si hacemos eso con caracteres no podremos hacerlo ya que no hay un orden a priori entre textos.
"a":"z"
Warning: NAs introduced by coercion
Warning: NAs introduced by coercion
Error in "a":"z": NA/NaN argument
Un atajo es el comando 1:n
, que nos devuelve lo mismo que seq(1, n)
1:7
[1] 1 2 3 4 5 6 7
Si el elemento inicial es mayor que el final, entenderá que la secuencia es en orden decreciente.
7:-3
[1] 7 6 5 4 3 2 1 0 -1 -2 -3
También podemos definir otro tipo de distancia (paso) entre consecutivos con el argumento by = ...
seq(1, 7, by = 0.5) # secuencia desde 1 a 7 de 0.5 en 0.5
[1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0
Otras veces nos interesará definir una secuencia con una longitud concreta
seq(1, 50, l = 7) # secuencia desde 1 a 50 de longitud 7
[1] 1.000000 9.166667 17.333333 25.500000 33.666667 41.833333 50.000000
Incluso puede nos interese generar un vector de n elementos repetidos
rep(0, 7) # vector de 7 ceros
[1] 0 0 0 0 0 0 0
Dado que internamente son guardados como números también podremos hacer esto con fechas.
seq(as_date("2023-09-01"), as_date("2023-09-10"), by = 1)
[1] "2023-09-01" "2023-09-02" "2023-09-03" "2023-09-04" "2023-09-05"
[6] "2023-09-06" "2023-09-07" "2023-09-08" "2023-09-09" "2023-09-10"
2.2.2 Vectores de caracteres
Un vector es una concatenación de elementos del mismo tipo, pero no tienen porque ser necesariamente números. Vamos a crear una frase de ejemplo.
<- "Me llamo Javi"
frase frase
[1] "Me llamo Javi"
length(frase)
[1] 1
En el caso anterior no era un vector, era un solo elemento de texto. Para crear un vector debemos usar de nuevo c()
y separar elementos entre comas
<- c("Me", "llamo", "Javi")
vector vector
[1] "Me" "llamo" "Javi"
length(vector)
[1] 3
¿Qué sucederá si concatenamos elementos de diferente tipo?
c(1, 2, "javi", "3", TRUE)
[1] "1" "2" "javi" "3" "TRUE"
Fíjate que como todos tienen que ser del mismo tipo, lo que hace R
es convertir todo a texto, violando la integridad del dato
c(3, 4, TRUE, FALSE)
[1] 3 4 1 0
Es importante entender que los valores lógicos en realidad están almacenados internamente como 0/1
2.2.3 Operaciones con vectores
Con los vectores numéricos podemos hacer las mismas operaciones aritméticas que con los números → un número es un vector (de longitud uno)
¿Qué sucederá si sumamos o restamos un valor a un vector?
<- c(1, 3, 5, 7)
x + 1 x
[1] 2 4 6 8
* 2 x
[1] 2 6 10 14
Salvo que indiquemos lo contrario, en R
las operaciones con vectores son siempre elemento a elemento
Los vectores también pueden interactuar entre ellos, así que podemos definir, por ejemplo, sumas de vectores (elemento a elemento)
<- c(2, 4, 6)
x <- c(1, 3, 5)
y + y x
[1] 3 7 11
Dado que la operación (por ejemplo, una suma) se realiza elemento a elemento, ¿qué sucederá si sumamos dos vectores de distinta longitud?
<- c(1, 3, 5, 7)
z + z x
Warning in x + z: longer object length is not a multiple of shorter object
length
[1] 3 7 11 9
Lo que hace es reciclar elementos: si tiene un vector de 4 elementos y sumamos otro de 3 elementos, lo que hará será reciclar del vector con menor longitud.
Una operación muy habitual es preguntar a los datos mediante el uso de condiciones lógicas. Por ejemplo, si definimos un vector de temperaturas…
¿Qué días hizo menos de 22 grados?
<- c(15, 20, 31, 27, 15, 29) x
< 22 x
[1] TRUE TRUE FALSE FALSE TRUE FALSE
Nos devolverá un vector lógico, en función de si cada elemento cumple o no la condición pedida (de igual longitud que el vector preguntado)
Si tuviéramos un dato ausente (por error del aparato ese día), la condición evaluada también sería NA
<- c(15, 20, NA, 31, 27, 7, 29, 10)
y < 22 y
[1] TRUE TRUE NA FALSE FALSE TRUE FALSE TRUE
Las condiciones lógicas pueden ser combinadas de dos maneras:
- Intersección: todas las condiciones concatenadas se deben cumplir (conjunción y con
&
) para devolver unTRUE
< 30 & x > 15 x
[1] FALSE TRUE FALSE TRUE FALSE TRUE
- Unión: basta con que al menos una se cumpla (conjunción o con
|
)
< 30 | x > 15 x
[1] TRUE TRUE TRUE TRUE TRUE TRUE
Además con any()
y all()
podemos comprobar que todos los elementos cumplen
any(x < 30)
[1] TRUE
all(x < 30)
[1] FALSE
2.2.3.1 Acceder a elementos
Otra operación muy habitual es la de acceder a elementos. La forma más sencilla es usar el operador [i]
(acceder al elemento i-ésimo)
<- c(20, 30, 33, NA, 61)
edades 3] # accedemos a la edad de la tercera persona edades[
[1] 33
Dado que un número no es más que un vector de longitud uno, esta operación también la podemos aplicar usando un vector de índices a seleccionar
<- c("hola", "qué", "tal", "estás", "?")
y c(1:2, 4)] # primer, segundo y cuarto elemento y[
[1] "hola" "qué" "estás"
Para acceder al último, sin preocuparnos de cuál es, podemos pasarle como índice la propia longitud x[length(x)]
Otras veces no querremos seleccionar sino eliminar algunos elementos. Deberemos repetir la misma operación pero con el signo - delante: el operador [-i]
no selecciona el elemento i-ésimo del vector sino que lo «des-selecciona»
y
[1] "hola" "qué" "tal" "estás" "?"
-2] y[
[1] "hola" "tal" "estás" "?"
En muchas ocasiones los queremos seleccionar o eliminar en base a condiciones lógicas, en función de los valores, así que pasaremos como índice la propia condición (recuerda, x < 2
nos devuelve un vector lógico)
<- c(15, 21, 30, 17, 45)
edades <- c("javi", "maría", "laura", "carla", "luis")
nombres < 18] # nombres de los menores de edad nombres[edades
[1] "javi" "carla"
2.2.3.2 Operaciones estadísticas
También podemos hacer uso de operaciones estadísticas como por ejemplo sum()
que, dado un vector, nos devuelve la suma de todos sus elementos.
<- c(1, -2, 3, -1)
x sum(x)
[1] 1
¿Qué sucede cuando falta un dato (ausente)?
<- c(1, -2, 3, NA, -1)
x sum(x)
[1] NA
Por defecto, si tenemos un dato ausente, la operación también será ausente. Para poder obviar ese dato, usamos un argumento opcional na.rm = TRUE
sum(x, na.rm = TRUE)
[1] 1
Como hemos comentado que los valores lógicos son guardados internamente como 0 y 1, podremos usarlos en operaciones aritméticas. Por ejemplo, si queremos averiguar el número de elementos que cumplen una condición (por ejemplo, menores que 3), los que lo hagan tendrán asignado un 1 (TRUE
) y los que no un 0 (FALSE
) , por lo que basta con sumar dicho vector lógico para obtener el número de elementos que cumplen
<- c(2, 4, 6)
x sum(x < 3)
[1] 1
Otra operación habitual que puede sernos útil es la suma acumulada con cumsum()
que, dado un vector, nos devuelve un vector a su vez con el primero, el primero más el segundo, el primero más el segundo más el tercero…y así sucesivamente.
<- c(1, 5, 2, -1, 8)
x cumsum(x)
[1] 1 6 8 7 15
¿Qué sucede cuando falta un dato (ausente)?
<- c(1, -2, 3, NA, -1)
x cumsum(x)
[1] 1 -1 2 NA NA
En el caso de la suma acumulada lo que sucede es que a partir de ese valor, todo lo acumulado posterior será ausente.
Otra operación habitual que puede sernos útil es la diferencia (con retardo) con diff()
que, dado un vector, nos devuelve un vector con el segundo menos el primero, el tercero menos el segundo, el cuarto menos el tercero…y así sucesivamente.
<- c(1, 8, 5, 3, 9, 0, -1, 5)
x diff(x)
[1] 7 -3 -2 6 -9 -1 6
Con el argumento lag =
podemos indicar el retardo de dicha diferencia (por ejemplo, lag = 3
implica que se resta el cuarto menos el primero, el quinto menos el segundo, etc)
<- c(1, 8, 5, 3, 9, 0, -1, 5)
x diff(x, lag = 3)
[1] 2 1 -5 -4 -4
Otras operaciones habituales son la media, mediana, percentiles, etc.
- Media: medida de centralidad que consiste en sumar todos los elementos y dividirlos entre la cantidad de elementos sumados. La más conocida pero la menos robusta: dado un conjunto, si se introducen valores atípicos o outliers (valores muy grandes o muy pequeños), la media se perturba con mucha facilidad.
<- c(165, 170, 181, 191, 150, 155, 167, NA, 173, 177)
x mean(x, na.rm = TRUE)
[1] 169.8889
- Mediana: medida de centralidad que consiste en ordenar los elementos y quedarse con el que ocupa la mitad.
<- c(165, 170, 181, 191, 150, 155, 167, 173, 177)
x median(x)
[1] 170
- Percentiles: medidas de posición (nos dividen en partes iguales los datos).
quantile(x) # por defecto percentiles 0-25-50-75-100
0% 25% 50% 75% 100%
150 165 170 177 191
quantile(x, probs = c(0.1, 0.4, 0.9))
10% 40% 90%
154.0 167.6 183.0
2.2.3.3 Ordenar vectores
Por último, una acción habitual es saber ordenar valores:
sort()
: devuelve el vector ordenado. Por defecto de menor a mayor pero condecreasing = TRUE
podemos cambiarlo
<- c(81, 7, 25, 41, 65, 20, 33, 23, 77)
edades sort(edades)
[1] 7 20 23 25 33 41 65 77 81
sort(edades, decreasing = TRUE)
[1] 81 77 65 41 33 25 23 20 7
order()
: devuelve el vector de índices que tendríamos que usar para tener el vector ordenado
order(x)
[1] 5 6 1 7 2 8 9 3 4
order(x)] x[
[1] 150 155 165 167 170 173 177 181 191
2.3 💻 Ejercicios
Intenta realizar los siguientes ejercicios sin mirar las soluciones
📝 Define una variable que guarde tu edad (llamada edad
) y otra con tu nombre (llamada nombre
)
Code
<- 33
edad <- "Javi" nombre
📝 Comprueba con dicha variable edad
si NO tiene 60 años o si se llama "Ornitorrinco"
(debes obtener variables lógicas como resultado)
Code
!= 60 # distinto de
edad == "Ornitorrinco" # igual a nombre
📝 ¿Por qué el código inferior da error?
+ nombre edad
Error in edad + nombre: non-numeric argument to binary operator
📝 Define otra variable llamada hermanos
que responda la pregunta «¿tienes hermanos?» y otra variable que almacene tu fecha de nacimiento (llamada fecha_nacimiento
).
Code
<- TRUE
hermanos
library(lubridate) # sino lo tenías ya cargado
<- as_date("1989-09-10") fecha_nacimiento
📝 Define otra variable con tus apellidos (llamada apellidos
) y usa glue()
para tener, en una sola variable llamada nombre_completo
, tu nombre y apellidos separando nombre y apellido por una coma
Code
<- "Álvarez Liébana"
apellidos glue("{nombre}, {apellidos}")
📝 De fecha_nacimiento
extrae el mes.
Code
month(fecha_nacimiento)
📝 Calcula los días que han pasado desde la fecha de tu nacimiento hasta hoy (con la fecha de nacimiento definida en el ejercicio 4).
Code
today() - fecha_nacimiento
📝 Define el vector x
como la concatenación de los 5 primeros números impares. Calcula la longitud del vector
Code
# Dos formas
<- c(1, 3, 5, 7, 9)
x <- seq(1, 9, by = 2)
x
length(x)
📝 Accede al tercer elemento de x
. Accede al último elemento (sin importar la longitud, un código que pueda ejecutarse siempre). Elimina el primer elemento.
Code
3]
x[length(x)]
x[-1] x[
📝 Obtén los elementos de x
mayores que 4. Calcula el vector 1/x
y guárdalo en una variable.
Code
> 4]
x[x <- 1/x
z z
📝 Crea un vector que represente los nombres de 5 personas, de los cuales uno es desconocido.
Code
<- c("Javi", "Sandra", NA, "Laura", "Carlos")
nombres nombres
📝 Encuentra del vector x
de ejercicios anteriores los elementos mayores (estrictos) que 1 Y ADEMÁS menores (estrictos) que 7. Encuentra una forma de averiguar si todos los elementos son o no positivos.
Code
> 1 & x < 7]
x[x all(x > 0)
📝 Dado el vector x <- c(1, -5, 8, NA, 10, -3, 9)
, ¿por qué su media no devuelve un número sino lo que se muestra en el código inferior?
<- c(1, -5, 8, NA, 10, -3, 9)
x mean(x)
[1] NA
📝 Dado el vector x <- c(1, -5, 8, NA, 10, -3, 9)
, extrae los elementos que ocupan los lugares 1, 2, 5, 6.
Code
<- c(1, -5, 8, NA, 10, -3, 9)
x c(1, 2, 5, 6)]
x[-2] x[
📝 Dado el vector x
del ejercicio anterior, ¿cuales tienen un dato ausente? Pista: las funciones is.algo()
comprueban si el elemento es tipo algo
(tabula)
Code
is.na(x)
📝 Define el vector x
como la concatenación de los 4 primeros números pares. Calcula el número de elementos de x
menores estrictamente que 5.
Code
< 5]
x[x sum(x < 5)
📝 Calcula el vector 1/x
y obtén la versión ordenada (de menor a mayor) de las dos formas posibles
Code
<- 1/x
z sort(z)
order(z)] z[
Code
min(x)
max(x)
📝 Encuentra del vector x
los elementos mayores (estrictos) que 1 y menores (estrictos) que 6. Encuentra una forma de averiguar si todos los elementos son o no negativos.
Code
> 1 & x < 7]
x[x all(x > 0)
3 Profundizando en textos
Aunque no podamos hacer operaciones aritméticas con ellos si serán importante algunas operaciones que podamos realizar con las cadenas de texto. Para eso usaremos el paquete {stringr}
(dentro del mismo «universo de paquetes» de {lubridate}
)
library(stringr)
Con dicho paquete vamos a trabajar particularmente con cuatro familias de funciones
Manipulación
Tratamiento de espacios
Búsqueda de patrones
3.1 Utilidades básicas
3.1.1 Longitud
La más obvia es una función que, dada una cadena de texto (un string) nos proporcione la longitud. Para ello podemos usar la función str_length()
str_length("abc")
[1] 3
Es importante advertir que cuenta tanto números como espacios, así como caracteres que no sean alfanuméricos.
str_length("abc 123 *")
[1] 9
Además si el texto es ausente devuelve ausente (recuerda: NA
es ausente, "NA"
es una cadena de texto más)
str_length(NA)
[1] NA
Las funciones del paquete están preparadas para ser vectorizadas lo que significa que si aplicamos una función a un vector de dos cadenas de texto la aplica a ambos de la misma forma.
str_length(c("abc", "defghi"))
[1] 3 6
3.1.2 Ordenar
Otra muy habitual es ordenar cadenas de texto (por orden alfabético). Para ello podemos usar la función str_order()
, distinguiendo ..._sort()
y ..._order()
como con los números
<- c("y", "i", "k")
x str_order(x)
[1] 2 3 1
str_sort(x)
[1] "i" "k" "y"
3.2 Manipulación
3.2.1 Extraer subcadenas
- Extraer subcadenas: dada una cadena de texto,
str_sub(texto, star = ..., end = ...)
nos extrae la subcadena desde la posiciónstar
hastaend
(si es negativo empieza a contar por detrás).
str_sub("abcd efg", star = 4, end = 6)
[1] "d e"
str_sub("abcd efg", star = 5)
[1] " efg"
str_sub("abcd efg", star = 4, end = -2)
[1] "d ef"
- Extraer subcadenas: la función
str_sub()
permite aplicarlo a de manera vectorial a múltiples cadenas de texto, e incluso usarla para asignar valores.
<- c("abcdef", "ghifjk")
x str_sub(x, star = 3, end = -2)
[1] "cde" "ifj"
str_sub(x, star = -1, end = -1)
[1] "f" "k"
# En ambas cadenas, sustituimos por * en la posición 2
str_sub(x, star = 2, end = 2) <- "*"
3.2.2 Duplicar cadenas
- Duplicar cadenas: con
str_dup(..., times = ...)
, dada una cadena de texto (o varias), podemos repetir una cadenatimes
veces.
str_dup("abc", times = 3)
[1] "abcabcabc"
<- c("abcdef", "ghifjk")
x str_dup(x, times = c(2, 5))
[1] "abcdefabcdef" "ghifjkghifjkghifjkghifjkghifjk"
3.2.3 Concatenar cadenas
- Concatenar cadenas: con
str_c
podemos concatenar distintas cadenas de texto (consep = ...
indicamos el caracter que hará de separador)
str_c("Buenos días", "Mi nombre es Javier")
[1] "Buenos díasMi nombre es Javier"
str_c("Buenos días", "Mi nombre es Javier", sep = ". ")
[1] "Buenos días. Mi nombre es Javier"
3.2.4 Mayúsculas y minúsculas
- Mayúsculas/minúsculas: con
str_to_...()
podemos convertir textos a mayúsculas (..._upper
), a minúsculas (..._lower
) y a título (..._title
, primera letra de cada palabra en mayúscula)
str_to_upper("me llamo Javi")
[1] "ME LLAMO JAVI"
str_to_lower("me llamo Javi")
[1] "me llamo javi"
str_to_title("me llamo Javi")
[1] "Me Llamo Javi"
3.2.5 Reemplazar
- Reemplazar:
str_replace()
busca un patrón dado en una cadena de texto y, si la encuentra, la sustituye pro otra de reemplazo
str_replace(c("javi", "sandra", "carlos"), pattern = "i", replacement = "*")
[1] "jav*" "sandra" "carlos"
. . .
Con str_replace_all()
reemplazamos todas las coincidencias (por defecto sino solo se reemplaza la primera)
str_replace(c("javi", "sandra", "carlos"), pattern = "a", replacement = "*")
[1] "j*vi" "s*ndra" "c*rlos"
str_replace_all(c("javi", "sandra", "carlos"), pattern = "a", replacement = "*")
[1] "j*vi" "s*ndr*" "c*rlos"
3.3 Espacios en blanco
3.3.1 Rellenar espacios
- Rellenar: la función
str_pad()
rellena una cadena con espacios (al inicio por defecto) para que tenga anchura indicada. Conside = "both"
como argumento extra nos añade en ambos lados. Conside = "right"
los añade al final. Conpad = ...
podemos decidir si queremos rellenar con otro tipo de caracter (espacio por defecto).
str_pad("abc", width = 6)
[1] " abc"
str_pad("abc", 12, side = "both")
[1] " abc "
str_pad("abc", 6, side = "right", pad = "*")
[1] "abc***"
. . .
Si width
es menor que la longitud de la cadena, no hace nada.
str_pad("abc", width = 2)
[1] "abc"
3.3.2 Eliminar espacios
- Eliminar espacios: con
str_trim()
podemos eliminar espacios en blanco al inicio y al final de la cadena. Si añadimosside = ...
podemos cambiar si queremos que solo los elimine al final o al inicio (por defecto, en ambos). Constr_squish()
cambiamos cualquier sucesión de espacios en blanco en medio del texto por uno solo (y elimina al inicio y final)
str_trim(" abcde fghi ")
[1] "abcde fghi"
str_trim(" abcde ")
[1] "abcde"
str_trim(" abcde ", side = "left")
[1] "abcde "
str_squish(" abcde fghi ")
[1] "abcde fghi"
3.4 Patrones
3.4.1 Detectar. Expresiones regulares.
- Detectar: con
str_detect()
podemos detectar si una cadena de texto contiene o no una secuencia de caracteres
str_detect(c("javi álvarez", "javi reyes", "sandra reyes"), pattern = "javi")
[1] TRUE TRUE FALSE
str_detect(c("javi álvarez", "javi reyes", "sandra reyes"), pattern = "reyes")
[1] FALSE TRUE TRUE
str_detect(c("javi álvarez", "javi reyes", "sandra reyes"), pattern = "carlos")
[1] FALSE FALSE FALSE
- Expresiones regulares: no solo vamos a poder detectar patrones simples sino que podemos hacer uso de las conocidas como expresiones regulares, indicándole por ejemplo que queremos localizar todo patrón que sea, al menos una letra
str_detect(c("a", "ab", "abc", "abcd"), pattern = "[a-z]")
[1] TRUE TRUE TRUE TRUE
. . .
Si tras los corchetes indicamos {n}
podemos detectar aquellas cadenas con n letras consecutivas
str_detect(c("a", "ab", "abc", "abcd"), pattern = "[a-z]{3}")
[1] FALSE FALSE TRUE TRUE
- Expresiones regulares: un buen manejo de estas expresiones puede sernos muy útil para, por ejemplo, detectar formatos correctos en DNI o números de teléfono (de Madrid, por ejemplo).
Vamos a considerar que un formato correcto de DNI es aquel seguido por 8 números ([0-9]{8}
) seguido directamente de una letra mayúscula ([A-Z]
).
str_detect(c("5055A198-W", "50508040W", "5050505W", "50508040-W"),
pattern = "[0-9]{8}[A-Z]")
[1] FALSE TRUE FALSE FALSE
. . .
Podemos buscar distintos patrones a la vez concatenándolos con una |
str_detect(c("5055A198-W", "50508040W", "5050505W", "50508040-W"),
pattern = "[0-9]{8}[A-Z]|[0-9]{8}[-][A-Z]")
[1] FALSE TRUE FALSE TRUE
3.4.2 Contar
- Contar patrones: con
str_count()
podemos contar cuantas veces aparece un mismo patrón
str_count(c("abcd defg", "ab defg", "ab cd"), pattern = "[a-z]{4}")
[1] 2 1 0
3.4.3 Localizar posiciones
- Localizar posiciones:
str_locate()
nos permite localizar la primera posición en la que se produce un patrón. Constr_locate_all()
obtenemos todos
str_locate(c("abcde abcd", "cba", "*a*"), pattern = "a")
start end
[1,] 1 1
[2,] 3 3
[3,] 2 2
str_locate_all(c("abcde abcd", "cba", "*a*"), pattern = "a")
[[1]]
start end
[1,] 1 1
[2,] 7 7
[[2]]
start end
[1,] 3 3
[[3]]
start end
[1,] 2 2
3.4.4 Extraer patrones
- Extraer patrones: con
str_extract()
podemos extraer patrones (constr_extract_all()
todos ellos) de una cadena de texto.
str_extract(c("DNI: 5050W", "DNI: 50558040W, DNI: 50558080-W", "DNI: 50558080-W"),
pattern = "[0-9]{8}[A-Z]|[0-9]{8}[-][A-Z]")
[1] NA "50558040W" "50558080-W"
str_extract_all(c("DNI: 5050W", "DNI: 50558040W, DNI: 50558080-W", "DNI: 50558080-W"),
pattern = "[0-9]{8}[A-Z]|[0-9]{8}[-][A-Z]")
[[1]]
character(0)
[[2]]
[1] "50558040W" "50558080-W"
[[3]]
[1] "50558080-W"
3.4.5 Dividir cadenas
- Dividir: con
str_split()
podemos localizar un patrón y dividir la cadena de texto siempre que aparezca (constr_split_fixed()
podemos dividir en un número concreto de trozos)
str_split(c("a-b-c", "ab-c-d-e"), pattern = "-")
[[1]]
[1] "a" "b" "c"
[[2]]
[1] "ab" "c" "d" "e"
str_split_fixed(c("a-b-c", "ab-c-d-e"), pattern = "-", n = 2)
[,1] [,2]
[1,] "a" "b-c"
[2,] "ab" "c-d-e"
. . .
Si usamos boundary()
como patrón podemos dividir en base a caracteres, frases, palabras, etc.
<- "Esto es una frase. Y esto otra."
x str_split(x, boundary("word"))
[[1]]
[1] "Esto" "es" "una" "frase" "Y" "esto" "otra"
str_split(x, boundary("sentence"))
[[1]]
[1] "Esto es una frase. " "Y esto otra."
3.5 💻 Tu turno (textos)
Intenta realizar los siguientes ejercicios sin mirar las soluciones
El dataset será discursos
(extraído de https://github.com/lirondos/discursos-de-navidad) donde están guardados los discursos navidad de los jefes de Estado de España desde 1946 hasta 2021 (en dictadura y en democracia).
load(file = "./datos/discursos.RData")
📝 Convierte todos los discurso a minúscula.
Code
# Convertimos a minúscula
$texto <- str_to_lower(discursos$texto) discursos
📝 Elimina signos de puntuación tales como “:”, “,”, “.”, “;”, “¡”, “!”, “¿” y “?”. Tras ello elimina espacios adelante, atrás y en medio si existen solo deja uno de ellos.
Code
# Eliminamos los signos de puntuación
$texto <-
discursosstr_remove_all(discursos$texto, pattern = "\\:|\\,|\\.|\\;|\\¡|\\!|\\¿|\\?")
# Tras ello eliminamos espacios adelante, atrás y en medio solo dejamos uno
$texto <- str_squish(discursos$texto) discursos
📝 Crea una nueva variable long
con la longitud de cada discurso
Code
# nueva variable
$long <- str_length(discursos$texto) discursos
📝 Añade una nueva variable n_words
con el nº palabras de cada discurso. Pista: si tras dividir cada discurso en palabras usas length()
te devolverá seguro 76 ya que lo ha guardado en un tipo de dato llamado lista. Para calcular la longitud de cada uno de los 76 elementos de la lista usaremos lengths()
Code
<- list("a" = 1:2, "b" = 1:3, "c" = 1:4) # Explicación length
lista length(lista)
lengths(lista)
# Dividimos y aplicamos lengths
$n_words <- lengths(str_split(discursos$texto, boundary("word"))) discursos
📝 Determina los 5 años con mayor longitud, y los 5 años con menor número de palabras
Code
# 5 años con mayor longitud (usamos order para obtener índices)
$year[order(discursos$long, decreasing = TRUE)[1:5]]
discursos
# 5 años con menor cantidad de palabras
$year[order(discursos$n_words)[1:5]] discursos
📝 Incorpora una nueva variable llamada spain
que calcule el número de veces que se dice “españoles”, “españolas” o “españa” en el discurso. Determina los 5 años dónde menos se menten dichas palabras
Code
# Contamos
$spain <- str_count(discursos$texto, pattern = "españoles|españolas|españa")
discursos
# Años con más menciones
$year[order(discursos$spain, decreasing = TRUE)[1:5]] discursos
📝 De los 76 años calcula el número de discursos en los que las palabras “mujer” o “mujeres” se nombren más que las palabras “hombre” u “hombres”
Code
sum(str_count(discursos$texto, pattern = "mujer|mujeres") >
str_count(discursos$texto, pattern = "hombre|hombres"))
📝 Detecta los discursos donde aparece “cataluña”, “catalanes”, “catalán” o “catalanas” y quédate de la base de datos solo con aquellos que lo cumpla
Code
str_detect(discursos$texto, pattern = "cataluña|catalanes|catalán|catalanas"), ] discursos[
4 Primeras base de datos
Cuando analizamos datos solemos tener varias variables de cada individuo: necesitamos una «tabla» que las recopile.
4.1 Primer intento: matrices
La opción más inmediata son las matrices: concatenación de variables del mismo tipo e igual longitud.
Imagina que tenemos estaturas y pesos de 4 personas. ¿Cómo crear un dataset con las dos variables?
La opción más habitual es usando cbind()
: concatenamos (bind) vectores en forma de columnas (c)
estaturas <- c(150, 160, 170, 180)
pesos <- c(63, 70, 85, 95)
datos_matriz <- cbind(estaturas, pesos)
datos_matriz
estaturas pesos
[1,] 150 63
[2,] 160 70
[3,] 170 85
[4,] 180 95
También podemos construir la matriz por filas con la función rbind()
(concatenar - bind - por filas - rows), aunque lo recomendable es tener cada variable en columna e individuo en fila como luego veremos.
rbind(estaturas, pesos) # Construimos la matriz por filas
[,1] [,2] [,3] [,4]
estaturas 150 160 170 180
pesos 63 70 85 95
Podemos «visualizar» la matriz con
View(matriz)
.Podemos comprobar las dimensiones con
dim()
,nrow()
yncol()
: las matrices son un tipo de datos tabulados (organizados en filas y columnas)
dim(datos_matriz)
[1] 4 2
nrow(datos_matriz)
[1] 4
ncol(datos_matriz)
[1] 2
También podemos «darle vuelta» (matriz transpuesta) con t()
.
t(datos_matriz)
[,1] [,2] [,3] [,4]
estaturas 150 160 170 180
pesos 63 70 85 95
Dado que ahora tenemos dos dimensiones en nuestros datos, para acceder a elementos con []
deberemos proporcionar dos índices separados por comas: índice de la fila y de la columna
2, 1] # segunda fila, primera columna datos_matriz[
estaturas
160
1, 2] # primera fila, segunda columna datos_matriz[
pesos
63
En algunas casos querremos obtener los datos totales de un individuo (una fila concreta pero todas las columnas) o los valores de toda una variable para todos los individuos (una columna concreta pero todas las filas). Para ello dejaremos sin rellenar uno de los índices
2, ] # segundo individuo datos_matriz[
estaturas pesos
160 70
1] # primera variable datos_matriz[,
[1] 150 160 170 180
Mucho de lo aprendido con vectores podemos hacerlo con matrices, así podemos por ejemplo acceder a varias filas y/o columnas haciendo uso de las secuencias de enteros 1:n
c(1, 3), 1] # primera variable para el primer y tercer individuo datos_matriz[
[1] 150 170
También podemos definir una matriz a partir de un vector numérico, reorganizando los valores en forma de matriz (sabiendo que los elementos se van colocando por columnas).
<- matrix(1:9, ncol = 3)
z z
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
Incluso podemos definir una matriz de valores constantes, por ejemplo de ceros (para luego rellenar)
matrix(0, nrow = 2, ncol = 3)
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 0 0 0
4.1.1 Operaciones con matrices
Con las matrices sucede como con los vectores: cuando aplicamos una operación aritmética lo hacemos elemento a elemento
/5 z
[,1] [,2] [,3]
[1,] 0.2 0.8 1.4
[2,] 0.4 1.0 1.6
[3,] 0.6 1.2 1.8
Para realizar operaciones en un sentido matricial deberemos añadir %%
, por ejemplo, para multiplicar matrices será %*%
* t(z) z
[,1] [,2] [,3]
[1,] 1 8 21
[2,] 8 25 48
[3,] 21 48 81
%*% t(z) z
[,1] [,2] [,3]
[1,] 66 78 90
[2,] 78 93 108
[3,] 90 108 126
También podemos realizar operaciones por columnas/filas sin recurrir a bucles con la función apply()
, y le indicaremos como argumentos
- la matriz
- el sentido de la operación (
MARGIN = 1
por filas,MARGIN = 2
por columnas) - la función a aplicar
- argumentos extra que necesite la función
Por ejemplo, para aplicar una media a cada variable, será mean
aplicada con MARGIN = 2
(misma función para cada columna)
# Media (mean) por columnas (MARGIN = 2)
apply(datos_matriz, MARGIN = 2, FUN = "mean")
estaturas pesos
165.00 78.25
4.2 💻 Tu turno (matrices)
Intenta realizar los siguientes ejercicios sin mirar las soluciones
📝 Modifica el código inferior para definir una matriz x
de unos, de 3 filas y 7 columnas.
<- matrix(0, nrow = 2, ncol = 3)
x x
Code
<- matrix(1, nrow = 3, ncol = 7)
x x
📝 A la matriz anterior, suma un 1 a cada número de la matriz y divide el resultado entre 5. Tras ello calcula su transpuesta
Code
<- (x + 1)/5
new_matrix t(new_matrix)
📝 ¿Por qué el código inferior nos devuelve dicho mensaje de aviso?
matrix(1:15, nrow = 4)
Warning in matrix(1:15, nrow = 4): data length [15] is not a sub-multiple or
multiple of the number of rows [4]
[,1] [,2] [,3] [,4]
[1,] 1 5 9 13
[2,] 2 6 10 14
[3,] 3 7 11 15
[4,] 4 8 12 1
📝 Define la matriz x <- matrix(1:12, nrow = 4)
. Tras ello obtén los datos del primer individuo, los datos de la tercera variable, y el elemento (4, 1)
.
Code
<- matrix(1:12, nrow = 4)
x 1, ] # primera fila
x[3] # tercera columna
x[, 4, 1] # elemento (4, 1) x[
📝 Define una matriz de 2 variables y 3 individuos tal que cada variable capture la estatura y la edad 3 personas, de manera que la edad de la segunda persona sea desconocida (ausente). Tras ello calcula la media de cada variable (¡nos debe de volver un número!)
Code
<- cbind("edad" = c(20, NA, 25), "estatura" = c(160, 165, 170))
datos apply(datos, MARGIN = 2, FUN = "mean", na.rm = TRUE) # media por columnas
📝 ¿Por qué devuelve error el código inferior? ¿Qué está mal?
<- cbind("edad" = c(15, 20, 25), "nombres" = c("javi", "sandra", "carlos"))
matriz matriz
edad nombres
[1,] "15" "javi"
[2,] "20" "sandra"
[3,] "25" "carlos"
+ 1 matriz
Error in matriz + 1: non-numeric argument to binary operator
4.3 Segundo intento: data.frame
Las matrices tienen el mismo problema que los vectores: si juntamos datos de distinto tipo, se perturba la integridad del dato ya que los convierte (fíjate en el código inferior: las edades y los TRUE/FALSE
los ha convertido a texto)
edades <- c(14, 24, NA)
soltero <- c(TRUE, NA, FALSE)
nombres <- c("javi", "laura", "lucía")
matriz <- cbind(edades, soltero, nombres)
matriz
edades soltero nombres
[1,] "14" "TRUE" "javi"
[2,] "24" NA "laura"
[3,] NA "FALSE" "lucía"
De hecho al no ser números ya no podemos realizar operaciones aritméticas
+ 1 matriz
Error in matriz + 1: non-numeric argument to binary operator
Para poder trabajar con variables de distinto tipo tenemos en R
lo que se conoce como data.frame: concatenación de variables de igual longitud pero que pueden ser de tipo distinto.
<- data.frame(edades, soltero, nombres)
tabla class(tabla)
[1] "data.frame"
tabla
edades soltero nombres
1 14 TRUE javi
2 24 NA laura
3 NA FALSE lucía
Dado que un data.frame
es ya un intento de «base de datos» las variables no son meros vectores matemáticos: tienen un significado y podemos (debemos) ponerles nombres que describan su significado
library(lubridate)
<-
tabla data.frame("edad" = edades, "estado" = soltero, "nombre" = nombres,
"f_nacimiento" = as_date(c("1989-09-10", "1992-04-01", "1980-11-27")))
tabla
edad estado nombre f_nacimiento
1 14 TRUE javi 1989-09-10
2 24 NA laura 1992-04-01
3 NA FALSE lucía 1980-11-27
¡TENEMOS NUESTRO PRIMER CONJUNTO DE DATOS! (estrictamente no podemos hablar de base de datos pero de momento como lo si fuesen). Puedes visualizarlo escribiendo su nombre en consola o con View(tabla)
4.3.1 Acceso a variables
Si queremos acceder a sus elementos, al ser de nuevo datos tabulados, podemos acceder como en las matrices (no recomendable): de nuevo tenemos dos índices (filas y columnas, dejando libre la que no usemos)
2, ] # segunda fila (todas sus variables) tabla[
edad estado nombre f_nacimiento
2 24 NA laura 1992-04-01
3] # tercera columna (de todos los individuos) tabla[,
[1] "javi" "laura" "lucía"
2, 1] # primera característica de la segunda persona tabla[
[1] 24
Pero también tiene las ventajas de una «base» de datos : podemos aceder a las variables por su nombre (lo recomendable ya que las variables pueden cambiar de posición y ahora sí tienen un significado), poniendo el nombre de la tabla seguido del símbolo $
(con el tabulador, nos aparecerá un menú de columnas a elegir)
4.3.2 Funciones de consulta
names()
: nos muestra los nombres de las variables
names(tabla)
[1] "edad" "estado" "nombre" "f_nacimiento"
dim()
: nos muestra las dimensiones (tambiénnrow()
yncol()
)
dim(tabla)
[1] 3 4
- Podemos acceder a las variables por su nombre
c(1, 3), "nombre"] tabla[
[1] "javi" "lucía"
$nombre[c(1, 3)] tabla
[1] "javi" "lucía"
4.3.3 Añadir variable
Si tenemos uno ya creado y queremos añadir una columna es tan simple como usar la función data.frame()
que ya hemos visto para concatenar la columna. Vamos añadir por ejemplo una nueva variable, el número de hermanos de cada individuo.
# Añadimos una nueva columna con nº de hermanos/as
<- c(0, 2, 3)
hermanos <- data.frame(tabla, "n_hermanos" = hermanos)
tabla tabla
edad estado nombre f_nacimiento n_hermanos
1 14 TRUE javi 1989-09-10 0
2 24 NA laura 1992-04-01 2
3 NA FALSE lucía 1980-11-27 3
4.4 Intento final: tibble
Las tablas en formato data.frame
tienen algunas limitaciones. La principal es que no permite la recursividad: imagina que definimos una base de datos con estaturas y pesos, y queremos una tercera variable con el IMC
data.frame("estatura" = c(1.7, 1.8, 1.6), "peso" = c(80, 75, 70),
"IMC" = peso / (estatura^2))
Error in data.frame(estatura = c(1.7, 1.8, 1.6), peso = c(80, 75, 70), : object 'peso' not found
En adelante usaremos el formato tibble
(data.frame mejorado) del paquete {tibble}
library(tibble)
<-
datos_tb tibble("estatura" = c(1.7, 1.8, 1.6), "peso" = c(80, 75, 70), "IMC" = peso / (estatura^2))
class(datos_tb)
[1] "tbl_df" "tbl" "data.frame"
datos_tb
# A tibble: 3 × 3
estatura peso IMC
<dbl> <dbl> <dbl>
1 1.7 80 27.7
2 1.8 75 23.1
3 1.6 70 27.3
<-
datos_tb tibble("estatura" = c(1.7, 1.8, 1.6), "peso" = c(80, 75, 70), "IMC" = peso / (estatura^2))
datos_tb
# A tibble: 3 × 3
estatura peso IMC
<dbl> <dbl> <dbl>
1 1.7 80 27.7
2 1.8 75 23.1
3 1.6 70 27.3
Las tablas en formato tibble
nos permitirá una gestión más ágil, eficiente y coherente de los datos, con 4 ventajas principales:
Metainformación: si te fijas en la cabecera, nos dice ya automáticamente el número de filas y columnas, y el tipo de cada variable
Recursividad: permite definir las variables secuencialmente (como hemos visto)
Consistencia: si accedes a una columna que no existe avisa con un warning
$invent datos_tb
Warning: Unknown or uninitialised column: `invent`.
NULL
- Por filas: crear por filas (copiar y pegar de una tabla) con
tribble()
tribble(~colA, ~colB,
"a", 1,
"b", 2)
# A tibble: 2 × 2
colA colB
<chr> <dbl>
1 a 1
2 b 2
El paquete {datapasta}
nos permite copiar y pegar tablas de páginas web y documentos sencillos
4.5 💻 Tu turno (tb/df)
Intenta realizar los siguientes ejercicios sin mirar las soluciones
📝 Carga del paquete {datasets}
el conjunto de datos airquality
(variables de la calidad del aire de Nueva York desde mayo hasta septiembre de 1973). ¿Es el conjunto de datos airquality de tipo tibble? En caso negativo, conviértelo a tibble (busca en la documentación del paquete en https://tibble.tidyverse.org/index.html).
Code
library(tibble)
class(datasets::airquality)
<- as_tibble(datasets::airquality) airquality_tb
📝 Una vez convertido a tibble
obtén el nombre de las variables y las dimensiones del conjunto de datos. ¿Cuántas variables hay? ¿Cuántos días se han medido?
Code
names(airquality_tb)
ncol(airquality_tb)
nrow(airquality_tb)
📝 Filtra solo los datos de la quinta observación
Code
5, ] airquality_tb[
📝 Filtra solo los datos del mes de agosto. ¿Cómo indicarle que queremos solo las filas que cumplan una condición concreta? (pista: en realidad todo son vectores “formateados”)
Code
$Month == 8, ] airquality_tb[airquality_tb
📝 Selecciona aquellos datos que no sean ni de julio ni de agosto.
Code
$Month != 7 & airquality_tb$Month != 8, ]
airquality_tb[airquality_tb!(airquality_tb$Month %in% c(7, 8)), ] airquality_tb[
📝 Modifica el siguiente código para quedarte solo con las variable de ozono y temperatura (sin importar qué posición ocupen)
3] airquality_tb[,
📝 Selecciona los datos de temperatura y viento de agosto.
Code
$Month == 8, c("Temp", "Wind")] airquality_tb[airquality_tb
📝 Traduce a castellano el nombre de las variables.
Code
names(airquality_tb) <- c("ozono", "rad_solar", "viento", "temp", "mes", "dia")
5 🐣 Caso práctico I: airquality (vectores)
En el paquete {datasets}
(ya instalado por defecto) tenemos diversos conjuntos de datos y uno de ellos es airquality
. Debajo te he extraído 3 variables de dicho dataset (fíjate que se hace con datos$variable
, ese dolar será importante).
Los datos capturan medidas diarias (n = 153 observaciones) de la calidad del aire en Nueva York, de mayo a septiembre de 1973. Se midieron 6 variables: niveles de ozono, radiación solar, viento, temperatura, mes y día.
library(datasets)
<- airquality$Temp
temperature <- airquality$Month
month <- airquality$Day day
5.1 Pregunta 1
¿Cómo averiguar qué representan los datos? Piensa algún comando que nos dé información sobre objetos en R, sabiendo que el nombre del dataset es
airquality
Code
? airquality
Haciendo uso de ? ...
podemos consultar en el panel de ayuda lo que significa el objeto.
5.2 Pregunta 2
Accede solo a los 5 primeros registros de temperaturas. Después accede al primero, segundo, quinto y décimo
Code
# secuencia de 1 a 5
1:5]
temperature[
# otra forma
c(1, 2, 3, 4, 5)]
temperature[
# primero, segundo, quinto y décimo
c(1, 2, 5, 10)] temperature[
5.3 Pregunta 3
Accede solo a los registros de temperaturas de mayo (tienes las temperaturas guardadas, piensa como acceder a ellos pero ahora usando una condición en lugar de índices concretos). Después accede a los elementos de mayo, abril y junio
Code
== 5]
temperature[month
# abril, mayo y junio
== 4 | month == 5 | month == 6]
temperature[month
# otra forma más legible: %in% nos comprueba si
# los valores están dentro de una lista permitida
%in% c(4, 5, 6)] temperature[month
5.4 Pregunta 4
¿Cuántos registros tenemos de mayo? ¿Y de abril?
Code
# Una forma para registros de mayo
sum(month == 5)
# Otra forma: la longitud de un vector
length(temperature[month == 5])
# ídem en abril
sum(month == 4)
5.5 Pregunta 5
Construye una nueva variable
date
con la fecha de cada registro (combinando año, mes y día), sabiendo que todos los datos son de 1973. Pista: para construir una fecha antes debes tener un vector de textos (por ejemplo, “1973-01-01”)
Code
# variable de tipo date
library(lubridate)
<- as_date(glue("{1973}-{month}-{day}")) dates
5.6 Pregunta 6
Crea una nueva variable
temp_celsius
con la temperatura en ºC (sabiendo que se calcula como \(celsius = (fahr - 32) * (5/9)\)). Tras ello calcula cuántos días de junio superaron los 30 grados ºC.
Code
# Temperatura en celsius
<- (temperature - 32) * (5/9)
temp_celsius
temp_celsius
# una forma
sum(temp_celsius[month == 6] > 30)
# otra forma
length(temp_celsius[month == 6 & temp_celsius > 30])
5.7 Pregunta 7
¿Cuál fue la media de temperatura del mes de agosto?
Code
# media en agosto
mean(temperature[month == 8], na.rm = TRUE)
mean(temp_celsius[month == 8], na.rm = TRUE)
5.8 Pregunta 8
Dado el vector de temperaturas de agosto, ordena las temperaturas (los más fríos primero, los más cálidos después). Hazlo de las dos maneras aprendidas. Usa después el paquete
{microbenchmark}
(instálalo, cárgalo y busca en la ayuda la funciónmicrobenchmark
de dicho paquete) para comparar cuál de las dos opciones es más rápida.
Code
# con sort
<- sort(temp_celsius[month == 8])
temp_sort
temp_sort
# con order
<- temp_celsius[month == 8][order(temp_celsius[month == 8])]
temp_ord
temp_ord
# microbenchmark
# install.packages("microbenchmark")
::microbenchmark(sort(temp_celsius[month == 8]),
microbenchmark== 8][order(temp_celsius[month == 8])],
temp_celsius[month times = 1000)
# ¡Es más eficiente datos[order(datos)] que sort(datos!
6 🐣 Caso práctico II: pinniped dataset
Del paquete {Biostatistics}
usaremos el conunto de datos pinniped
, que guarda los datos de peso de cuerpo y cerebro (desagregado por sexo y mono/poligamia) de 33 especies de mamíferos marinos.
::pinniped Biostatistics
Species Male_brain_g Female_brain_g Male_mass_Kg
1 Monachus schauinslandi 370.0 NA 173.0
2 Monachus monachus 480.0 480.0 260.0
3 Mirounga angustirostris 700.0 640.0 2275.0
4 Mirounga leonina 1431.3 898.8 3510.0
5 Leptonychotes weddelli 535.0 637.5 450.0
6 Ommatophoca rossi 425.0 530.0 153.8
7 Lobodon carcinophagus 578.2 538.8 220.5
8 Hydrurga leptonyx 765.0 660.0 324.0
9 Cystophora cristata 480.0 430.0 343.2
10 Erignathus barbatus NA 460.0 312.5
11 Halichoerus grypus 342.5 272.5 233.0
12 Phoca groenlandica 297.5 252.5 145.0
13 Phoca fasciata 257.5 240.0 94.8
14 Phoca largha 257.5 250.0 97.0
15 Phoca caspica 165.0 160.0 70.5
16 Phoca sibirica 185.0 190.0 89.5
17 Phoca hispida 229.3 220.0 84.0
18 Phoca vitulina 362.3 265.0 97.1
19 Zalophus californianus 405.0 361.5 244.5
20 Eumetopias jubatus 747.5 575.0 1000.0
21 Otaria byronia 546.3 470.0 300.0
22 Neophoca cinerea 440.0 337.5 300.0
23 Phocarctos hookeri 417.5 370.0 364.0
24 Callorhinus ursinus 355.0 302.5 140.0
25 Arctocephalus townsendi NA NA 112.0
26 Arctocephalus philippii 415.0 NA 140.0
27 Arctocephalus galapagoensis 302.5 280.0 64.5
28 Arctocephalus australis 350.0 265.0 91.0
29 Arctocephalus forsteri 340.0 300.0 125.0
30 Arctocephalus gazella 360.0 320.0 155.0
31 Arctocephalus tropicalis 322.5 330.0 152.5
32 Arctocephalus pusillus 401.3 337.5 263.0
33 Odobenus rosmarus 1303.0 1340.5 1233.0
Female_mass_Kg Mate_type
1 272.2 mono
2 275.0 mono
3 488.0 poly
4 565.7 poly
5 447.0 poly
6 164.0 mono
7 224.0 mono
8 367.0 mono
9 222.5 mono
10 326.0 mono
11 205.8 poly
12 139.0 mono
13 80.4 mono
14 71.3 mono
15 55.0 mono
16 85.0 mono
17 81.2 mono
18 85.2 mono
19 81.0 poly
20 287.5 poly
21 144.0 poly
22 78.6 poly
23 114.7 poly
24 33.3 poly
25 49.6 poly
26 48.1 poly
27 27.4 poly
28 48.5 poly
29 38.1 poly
30 45.0 poly
31 50.0 poly
32 64.1 poly
33 811.5 poly
6.1 Pregunta 1
Comprueba si los datos están en formato tibble. En caso negativo conviértelo.
Code
# chequeamos si es tibble
library(tibble)
is_tibble(Biostatistics::pinniped)
# Convertimos a tibble
<- as_tibble(Biostatistics::pinniped) pinniped_tb
6.2 Pregunta 2
¿Cuántos registros hay? ¿Cuántas variables? ¿De qué tipo es cada una? ¿Cuáles son sus nombres?
Code
nrow(pinniped_tb)
ncol(pinniped_tb)
names(pinniped_tb)
6.3 Pregunta 3
Incorpora una variable nueva llamada
phoca
que sea de tipo lógico y que nos diga si una especie es de la categoríaPhoca
o no.
Code
# tienes más funcionalidades con textos del paquete stringr
# arriba en el workbook
$phoca <- str_detect(pinniped_tb$Species == "Phoca") pinniped_tb
6.4 Pregunta 4
¿A qué sexo le pesa más el cerebro: a las hembras o a los machos?
Code
# ¿a quién le pesa más el cerebro?
mean(pinniped_tb$Male_brain_g, na.rm = TRUE) >
mean(pinniped_tb$Female_brain_g, na.rm = TRUE)
6.5 Pregunta 5
¿A quienes les pesa más el cuerpo a los monógamos o a los polígamos? Recuerda que tienes los pesos divididos por sexos en variables distintas que tendrás que juntar de alguna forma
Code
# ¿a quién le pesa más el cerebro?
mean(c(pinniped_tb$Male_mass_Kg[pinniped_tb$Mate_type == "mono"],
$Female_mass_Kg[pinniped_tb$Mate_type == "mono"])) >
pinniped_tbmean(c(pinniped_tb$Male_mass_Kg[pinniped_tb$Mate_type == "poly"],
$Female_mass_Kg[pinniped_tb$Mate_type == "poly"])) pinniped_tb
6.6 Pregunta 6
Incopora una nueva variable llamada
dif_m_f
que represente la diferencia entre el peso del cerebro entre machos y hembras (machos - hembras) para cada especie.
Code
$dif_m_f <- pinniped_tb$Male_brain_g - pinniped_tb$Female_brain_g
pinniped_tb pinniped_tb
7 🐣 Caso práctico III: salto de longitud
Para practicar textos y tibbles vamos a usar el dataset salto_longitud.csv
que tienes guardado en la carpeta de datos, que guarda cómo ha progresado el récord de salto de longitud masculino. Dicha tabla ha sido extraída directamente de la wikipedia https://en.wikipedia.org/wiki/Men%27s_long_jump_world_record_progression#Low_altitude_record_progression_1965%E2%80%931991
load(file = "./datos/salto_longitud.RData")
Aunque más adelante veremos como extraerlo, puedes ver debajo si quieres el código que se ha usado para extraer la tabla
Code
library(rvest)
library(tidyverse)
<- 'https://en.wikipedia.org/wiki/Men%27s_long_jump_world_record_progression'
wiki_jump <- read_html(wiki_jump)
long_jump <- html_table(html_node(long_jump, 'table')) salto_longitud
7.1 Pregunta 1
¿Cuántos registros hay? ¿Cuántas variables? ¿De qué tipo es cada una? ¿Cuáles son sus nombres? ¿Qué crees que significan?
Code
nrow(salto_longitud)
ncol(salto_longitud)
names(salto_longitud)
# tenemos 19 récords y 5 variables: la marca (en metros, pies y pulgadas), el viento que hacía, la persona que batió el record, el lugar y la fecha.
7.2 Pregunta 2
Crea una nueva variable llamada
Mark
(sustituye a la anterior) en la que hayas extraído la marca en metros (y convertida a número). Práctica antes con un valor de juguete, por ejemplo, si tuviésemos `x <- “7.61 m (24 ft 11+1/2 in)”, ¿cómo podríamos quedarnos solo con la marca en metros?
Code
# Sabemos que el patrón es 1 número, un ., y dos decimales, así que
# usamos str_extract para extraer ese patrón que le indicamos
# con una expresión regular.
$Mark <-
salto_longitudstr_extract(salto_longitud$Mark, pattern = "[0-9][.][0-9]{2}")
# Tras ello convertimos a número
$Mark <- as.numeric(salto_longitud$Mark)
salto_longitud
# importante: ahora en la cabecera debajo de mark pone <dbl>, es un número
salto_longitud
7.3 Pregunta 3
Crea una nueva variable llamada
Wind
(sustituye la original) en la que tengas el viento convertido a número.
Code
$Wind <- as.numeric(salto_longitud$Wind)
salto_longitud salto_longitud
¿En cuántos récords no había viento? ¿En cuántos no tenemos registros de dicha medida?
Code
# Con viento 0
sum(salto_longitud$Wind == 0, na.rm = TRUE)
# Sin registros
sum(is.na(salto_longitud$Wind))
# importante: ausente y 0 no es lo mismo
7.4 Pregunta 4
Crea una nueva variable llamada
country
que extraiga las siglas de los países de la variableAthlete
Code
# Primero extraemos con los paréntesis (es más fácil detectarlo)
$country <-
salto_longitudstr_extract(salto_longitud$Athlete, pattern = "[(][A-Z]{3}[)]")
# Luego los eliminamos los ( y los ) (de ahí la barra vertical en medio)
# Fíjate que para que entienda que es un caracter especial (paréntesis)
# se lo pasamos como \\( y como \\). Ppasaría lo mismo si queremos detectar,
# por ejemplo, un punto, con \\.
$country <- str_remove_all(salto_longitud$country, "\\(|\\)") salto_longitud
7.5 Pregunta 5
Ordena la tabla por orden alfabético de las siglas
Code
<- salto_longitud[order(salto_longitud$country), ] salto_longitud
7.6 Pregunta 6
Reemplaza la variable
Date
por la fecha correcta en formato fecha
Code
# primero eliminamos todos los [?]
$Date <-
salto_longitudstr_remove_all(salto_longitud$Date, pattern = "\\[[0-9]{1}\\]")
# luego atendemos el caso particualr de [note 1]
$Date <-
salto_longitudstr_remove_all(salto_longitud$Date, pattern = "\\[note 1\\]")
# tras depurar convertimos a fecha
$Date <- dmy(salto_longitud$Date)
salto_longitud salto_longitud
7.7 Pregunta 7
¿Cuántos récords no son de Estados Unidos? ¿Qué porcentaje representa respecto al total?
Code
# total absoluto
sum(salto_longitud$country != "USA")
# porcentaje relativo
100*sum(salto_longitud$country != "USA") / length(salto_longitud$country)
7.8 Pregunta 8
Para cada récord, calcula los metros de mejora respecto al anterior, y guárdalo en la base de datos. Pista: prueba bien la función
diff()
y chequea la longitud de lo que te devuelve.
Code
# Usamos la función diff que, dado un vector, calcula las diferencias
# fíjate que el primero no conocemos la mejora (ya que no hay registro previo)
$mejora <- c(NA, diff(salto_longitud$Mark)) salto_longitud