Esta colección muestra los comandos más importantes del lenguaje de programación R, cada uno con un breve ejemplo. Las líneas que empiezan por #> muestran la salida de R. Todos los ejemplos funcionan a partir de R 4.1.
Conceptos básicos
Comentarios
R ignora todo lo que va después de # hasta el final de la línea.
# Soy un comentario
x <- 5 # Un comentario también puede ir detrás de un comandoSalida en la consola
print() muestra un objeto tal como lo presenta R, con el número de posición [1] delante. cat() muestra el texto sin más. sprintf() inserta valores en una plantilla de texto.
print("Hello World")
#> [1] "Hello World"
cat("Hello", "World", "\n")
#> Hello World
cat(sprintf("Pi es aproximadamente %.2f\n", pi))
#> Pi es aproximadamente 3.14
message("Listo") # mostrar un aviso, RStudio lo muestra en rojo
#> ListoAyuda sobre un comando
?which # página de ayuda del comando which
help(which) # lo mismo
??"regression" # búsqueda de texto en todas las páginas de ayuda instaladas
apropos("which") # todos los comandos cuyo nombre contiene "which"
example(which) # ejecutar los ejemplos de la página de ayuda
args(paste) # mostrar los parámetros de un comandoHistorial de comandos y salir de R
history() # mostrar los últimos comandos introducidos
q() # salir de R, en RStudio termina la sesiónVersión y sesión
R.version.string # versión de R instalada
sessionInfo() # versión, sistema operativo y paquetes cargadosDirectorio de trabajo y archivos
Mostrar y cambiar el directorio de trabajo
R lee y escribe los archivos sin ruta en el directorio de trabajo. También en Windows las carpetas se separan con / o con \\ doble. Quien trabaja con proyectos en RStudio rara vez necesita setwd(), porque RStudio fija el directorio de trabajo en la carpeta del proyecto.
getwd() # directorio de trabajo actual
setwd("C:/Users/aaron/Proyectos") # fijar un nuevo directorio de trabajoArchivos y carpetas
list.files() # archivos del directorio de trabajo
list.files("datos", pattern = "\\.csv$") # solo archivos CSV de la carpeta datos
file.exists("datos/clientes.csv") # TRUE si el archivo existe
dir.create("resultados") # crear una carpeta nueva
file.path("datos", "clientes.csv") # construir una ruta: "datos/clientes.csv"Guardar y cargar objetos
saveRDS() guarda un único objeto. Al cargarlo con readRDS(), uno mismo elige el nombre. save() guarda varios objetos junto con sus nombres, load() los restaura con los mismos nombres.
saveRDS(myTable, file = "myTable.rds")
myTable <- readRDS("myTable.rds")
save(myTable, myList, file = "objetos.RData")
load("objetos.RData")
savehistory("comandos.Rhistory") # escribir el historial de comandos en un archivoLeer y escribir archivos CSV
Los archivos CSV en español suelen separar las columnas con punto y coma y escribir los decimales con coma. Para ellos existen read.csv2() y write.csv2(). read.csv() y write.csv() están pensados para archivos con coma como separador y punto como signo decimal.
clientes <- read.csv2("C:/datos/clientes.csv", encoding = "UTF-8")
clientes <- read.csv("C:/datos/clientes.csv")
clientes <- read.table("C:/datos/clientes.csv", header = TRUE, sep = ";", dec = ",")
write.csv2(clientes, "clientes_nuevo.csv", row.names = FALSE)
# Más rápido con archivos grandes: paquete readr
library(readr)
clientes <- read_csv2("C:/datos/clientes.csv")Paquetes
Instalar y actualizar paquetes
install.packages("ggplot2")
install.packages(c("dplyr", "readr", "tidyr"))
update.packages(ask = FALSE) # actualizar todos los paquetes instalados
remove.packages("ggplot2") # eliminar un paqueteUna lista de todos los paquetes del repositorio oficial CRAN está disponible en cran.r-project.org/web/packages.
Cargar un paquete
library(ggplot2) # error si falta el paquete
require(ggplot2) # devuelve FALSE si falta el paquete
dplyr::filter(iris, Sepal.Length > 7) # llamar a un comando sin cargar el paqueteMostrar paquetes instalados y cargados
.libPaths() # carpetas donde están instalados los paquetes
Sys.getenv("R_LIBS_USER") # carpeta personal de paquetes
installed.packages()[, "Version"] # todos los paquetes instalados con su versión
library() # paquetes instalados en una ventana aparte
(.packages()) # paquetes cargados actualmente
getOption("defaultPackages") # paquetes que R carga al iniciar
packageVersion("ggplot2") # versión de un paqueteCargar paquetes automáticamente al iniciar
R ejecuta en cada inicio los comandos del archivo .Rprofile de la carpeta personal. El archivo se puede abrir directamente desde R:
file.edit("~/.Rprofile")Contenido del archivo, por ejemplo:
library(ggplot2)
library(dplyr)Un script que depende de paquetes cargados automáticamente no funciona en otro ordenador. Por eso library() debe seguir al principio de cada script.
El archivo Rprofile.site de la carpeta etc de la instalación de R vale para todos los usuarios y requiere permisos de administrador para modificarlo. Cada nueva versión de R trae su propio Rprofile.site, así que los cambios hay que volver a introducirlos tras cada actualización. Por eso el .Rprofile personal es la mejor opción.
Variables
Asignar un valor
a <- 1 # notación habitual
b = 2 # también funciona
3 -> c # asignación hacia la derecha, poco usada
assign("d", 4) # pasar el nombre como texto
`<-`(e, 5) # asignación como llamada a función
c(a, b, c, d, e)
#> [1] 1 2 3 4 5Mostrar y eliminar variables
var1 <- 10; var2 <- 20; nombre <- "R"; .oculta <- 30
ls() # todas las variables
#> [1] "nombre" "var1" "var2"
ls(pattern = "var") # variables cuyo nombre contiene "var"
#> [1] "var1" "var2"
ls(all.names = TRUE) # también las variables que empiezan por punto
#> [1] ".oculta" "nombre" "var1" "var2"
rm(var1) # eliminar una variable
exists("var1")
#> [1] FALSE
rm(list = ls()) # eliminar todas las variablesComparar valores
El ordenador calcula los números decimales con errores de redondeo. Por eso se comparan con all.equal() en lugar de ==.
a <- 1; b <- 2
a == b
#> [1] FALSE
0.1 + 0.2 == 0.3
#> [1] FALSE
isTRUE(all.equal(0.1 + 0.2, 0.3))
#> [1] TRUE
identical(c(1, 2), c(1, 2))
#> [1] TRUETipos de datos
Tipos básicos
class(TRUE) # logical: TRUE o FALSE
#> [1] "logical"
class(42L) # integer: número entero, se reconoce por la L
#> [1] "integer"
class(42) # numeric: número con decimales
#> [1] "numeric"
class(1 + 2i) # complex: número complejo
#> [1] "complex"
class("Hola") # character: texto
#> [1] "character"
class(as.raw(255)) # raw: bytes sueltos, salida en hexadecimal
#> [1] "raw"
as.raw(255)
#> [1] ff
typeof(42) # tipo de almacenamiento interno
#> [1] "double"Estructuras de datos
| Estructura | Contenido | Se crea con |
|---|---|---|
| Vector | valores del mismo tipo en fila | c() |
| Lista | valores de cualquier tipo, también otras listas | list() |
| Matriz | valores del mismo tipo en filas y columnas | matrix() |
| Array | valores del mismo tipo en cualquier número de dimensiones | array() |
| Factor | categorías con valores fijos | factor() |
| Data frame | tabla, cada columna tiene su propio tipo | data.frame() |
Comprobar y convertir tipos
is.numeric(42)
#> [1] TRUE
is.character("42")
#> [1] TRUE
as.numeric("42") + 1
#> [1] 43
as.integer(3.9) # corta los decimales
#> [1] 3
as.character(42)
#> [1] "42"
as.logical(c("TRUE", "no", "T"))
#> [1] TRUE NA TRUE
as.numeric("abc") # no convertible: NA con aviso
#> Warning: NAs introducidos por coerción
#> [1] NAValores ausentes
NA representa un valor ausente, NULL un objeto vacío, NaN un resultado de cálculo no válido e Inf el infinito.
x <- c(4, NA, 7)
is.na(x)
#> [1] FALSE TRUE FALSE
sum(x) # NA en cuanto falta un valor
#> [1] NA
sum(x, na.rm = TRUE) # omitir los valores ausentes
#> [1] 11
0 / 0
#> [1] NaN
1 / 0
#> [1] Inf
length(NULL)
#> [1] 0Operadores
Operadores aritméticos
| Operador | Significado | Ejemplo | Resultado |
|---|---|---|---|
+ - * / | operaciones básicas | 7 / 2 | 3.5 |
^ | potencia | 2 ^ 3 | 8 |
%% | resto de una división (módulo) | 7 %% 2 | 1 |
%/% | división entera | 7 %/% 2 | 3 |
Los operadores aritméticos actúan sobre cada elemento de un vector:
a <- c(4, 8, 12)
b <- c(12, 8, 4)
a %% b # resto de la división a / b
#> [1] 4 0 0
a %/% b # cuántas veces cabe b entero en a
#> [1] 0 1 3
c(2, 3, 4) ^ c(3, 2, 2) # 2*2*2, 3*3, 4*4
#> [1] 8 9 16Operadores de comparación
== igual, != distinto, < menor, > mayor, <= menor o igual, >= mayor o igual.
a <- c(4, 8, 12)
b <- c(12, 8, 4)
a > b
#> [1] FALSE FALSE TRUE
a == b
#> [1] FALSE TRUE FALSEOperadores lógicos
Los operadores & (y), | (o) y ! (no) comprueban los vectores elemento a elemento. && y || comprueban exactamente un valor y se usan en condiciones como if. El número 0 cuenta como FALSE, cualquier otro número como TRUE.
a <- c(3, 0, TRUE, FALSE, 0)
b <- c(4, 0, FALSE, FALSE, 0.1)
a & b
#> [1] TRUE FALSE FALSE FALSE FALSE
a | b
#> [1] TRUE FALSE TRUE FALSE TRUE
!c(TRUE, FALSE)
#> [1] FALSE TRUE
xor(TRUE, FALSE) # exactamente uno de los dos es TRUE
#> [1] TRUE
any(a > 2) # al menos un valor cumple la condición
#> [1] TRUE
all(a > 2) # todos los valores cumplen la condición
#> [1] FALSEOperadores de asignación
| Operador | Significado |
|---|---|
<- y = | asignar un valor a la variable de la izquierda |
-> | asignar un valor a la variable de la derecha |
<<- y ->> | asignar un valor a una variable fuera de la función actual |
contador <- 0
incrementar <- function() contador <<- contador + 1
incrementar(); incrementar()
contador
#> [1] 2Otros operadores
1:5 # secuencia de números
#> [1] 1 2 3 4 5
3 %in% c(1, 2, 3) # ¿está el valor en el vector?
#> [1] TRUE
matrix(1:4, 2) %*% matrix(1:4, 2) # multiplicación de matrices
#> [,1] [,2]
#> [1,] 7 15
#> [2,] 10 22
c(1, 4, 9) |> sqrt() |> sum() # pipe: pasar el resultado al siguiente comando
#> [1] 6Vectores
Crear un vector
c(1, 2, c(3, 4), 5) # los vectores anidados se aplanan
#> [1] 1 2 3 4 5
1:10
#> [1] 1 2 3 4 5 6 7 8 9 10
seq(0, 100, by = 25)
#> [1] 0 25 50 75 100
seq(1, 2, length.out = 5)
#> [1] 1.00 1.25 1.50 1.75 2.00
rep(0, 5)
#> [1] 0 0 0 0 0
rep(c("a", "b"), times = 2)
#> [1] "a" "b" "a" "b"
rep(c("a", "b"), each = 2)
#> [1] "a" "a" "b" "b"
c("hola", 0, TRUE) # un texto convierte todos los valores en texto
#> [1] "hola" "0" "TRUE"Acceder a elementos
La numeración empieza en 1.
dias <- c("lu", "ma", "mi", "ju", "vi", "sa", "do")
dias[3] # tercer elemento
#> [1] "mi"
dias[1:3] # del primer al tercer elemento
#> [1] "lu" "ma" "mi"
dias[c(1, 7)] # primer y séptimo elemento
#> [1] "lu" "do"
dias[-c(2, 3)] # todo excepto el segundo y el tercer elemento
#> [1] "lu" "ju" "vi" "sa" "do"
dias[c(TRUE, FALSE)] # uno de cada dos elementos
#> [1] "lu" "mi" "vi" "do"
dias[length(dias)] # último elemento
#> [1] "do"
head(dias, 2)
#> [1] "lu" "ma"
tail(dias, 2)
#> [1] "sa" "do"Nombrar y seleccionar elementos
precios <- c(pan = 3.5, leche = 1.2, queso = 4.9)
precios["leche"]
#> leche
#> 1.2
precios[precios > 2] # solo los valores que cumplen la condición
#> pan queso
#> 3.5 4.9
which(precios > 2) # posiciones de esos valores
#> pan queso
#> 1 3
names(precios)
#> [1] "pan" "leche" "queso"Calcular con vectores
Si las longitudes son distintas, R repite el vector más corto. El ejemplo calcula 1*1, 2*2, 3*3, 1*4, 2*5, 3*6.
a <- c(1, 2, 3)
b <- c(1, 2, 3, 4, 5, 6)
a * b
#> [1] 1 4 9 4 10 18
a + 10
#> [1] 11 12 13Ordenar y analizar
x <- c(4, 1, 9, 4, 10, 18)
length(x)
#> [1] 6
sort(x)
#> [1] 1 4 4 9 10 18
sort(x, decreasing = TRUE)
#> [1] 18 10 9 4 4 1
order(x) # posiciones en orden ascendente
#> [1] 2 1 4 3 5 6
rev(x) # invertir el orden
#> [1] 18 10 4 9 1 4
unique(x)
#> [1] 4 1 9 10 18
duplicated(x)
#> [1] FALSE FALSE FALSE TRUE FALSE FALSE
table(x) # frecuencia de cada valor
#> x
#> 1 4 9 10 18
#> 1 2 1 1 1
cumsum(x) # suma acumulada
#> [1] 4 5 14 18 28 46Unir vectores
x <- c("a", "b")
y <- c("A", "B")
c(x, y) # en un vector más largo
#> [1] "a" "b" "A" "B"
paste(x, y) # elemento a elemento en texto
#> [1] "a A" "b B"
rbind(x, y) # como filas de una matriz
#> [,1] [,2]
#> x "a" "b"
#> y "A" "B"
cbind(x, y) # como columnas de una matriz
#> x y
#> [1,] "a" "A"
#> [2,] "b" "B"Cadenas de texto
Longitud y partes
a <- "abcdefg"
nchar(a)
#> [1] 7
substr(a, 3, 5)
#> [1] "cde"
toupper("Hello")
#> [1] "HELLO"
tolower("Hello")
#> [1] "hello"
trimws(" espacios ")
#> [1] "espacios"Las comillas simples y dobles son equivalentes. R muestra el texto siempre con comillas dobles.
'hello'
#> [1] "hello"Unir texto
a <- "Hello"
b <- "How"
c <- "are you?"
paste(a, b, c)
#> [1] "Hello How are you?"
paste(a, b, c, sep = "@")
#> [1] "Hello@How@are you?"
paste0(a, b, c) # sin separador
#> [1] "HelloHoware you?"
paste(c("a", "b", "c"), collapse = "-") # vector en un solo texto
#> [1] "a-b-c"
sprintf("%s tiene %d puntos", "Linda", 42L)
#> [1] "Linda tiene 42 puntos"Dar formato al texto
format(18, width = 5)
#> [1] " 18"
format("Hello", width = 9, justify = "centre") # "left", "right", "centre"
#> [1] " Hello "
formatC(7, width = 3, flag = "0") # ceros a la izquierda
#> [1] "007"Buscar, reemplazar y dividir
frutas <- c("Albaricoque", "Pera", "Aguacate")
grepl("^A", frutas) # ¿empieza el texto por A?
#> [1] TRUE FALSE TRUE
startsWith(frutas, "A") # lo mismo sin expresión regular
#> [1] TRUE FALSE TRUE
sub("a", "4", "Banana") # reemplazar la primera coincidencia
#> [1] "B4nana"
gsub("a", "4", "Banana") # reemplazar todas las coincidencias
#> [1] "B4n4n4"
strsplit("a,b,c", ",")[[1]] # dividir un texto por un carácter
#> [1] "a" "b" "c"
texto <- "Pedido 4711 del lunes"
regmatches(texto, regexpr("[0-9]+", texto)) # primer número del texto
#> [1] "4711"Factores
Un factor almacena categorías. Los valores posibles se llaman niveles (levels).
v <- c("thomas", "thomas", "thomas", "linda")
class(v)
#> [1] "character"
levels(v)
#> NULL
v <- factor(v)
class(v)
#> [1] "factor"
typeof(v) # internamente R guarda los niveles como números enteros
#> [1] "integer"
levels(v)
#> [1] "linda" "thomas"
nlevels(v)
#> [1] 2
table(v)
#> v
#> linda thomas
#> 1 3Orden de los niveles
Si no se indica nada, R ordena los niveles alfabéticamente. Con levels se fija el orden, por ejemplo para los ejes de un gráfico.
a <- factor(c("Thomas", "Linda", "Felix", "Sam", "Linda"))
levels(a)
#> [1] "Felix" "Linda" "Sam" "Thomas"
a <- factor(a, levels = c("Linda", "Sam", "Thomas", "Felix"))
levels(a)
#> [1] "Linda" "Sam" "Thomas" "Felix"Factores ordenados
talla <- factor(c("M", "S", "L", "M"), levels = c("S", "M", "L"), ordered = TRUE)
talla
#> [1] M S L M
#> Levels: S < M < L
talla > "S"
#> [1] TRUE FALSE TRUE TRUECrear y limpiar factores
gl(n, k) crea n niveles que aparecen k veces seguidas cada uno.
gl(2, 4, labels = c("Linda", "Sam"))
#> [1] Linda Linda Linda Linda Sam Sam Sam Sam
#> Levels: Linda Sam
f <- factor(c("a", "b", "c"))[1:2]
droplevels(f) # eliminar los niveles que ya no se usan
#> [1] a b
#> Levels: a b
as.character(f) # volver a texto
#> [1] "a" "b"Desde R 4.0, data.frame() ya no convierte el texto en factores automáticamente. Quien necesite el comportamiento antiguo debe indicar stringsAsFactors = TRUE.
df <- data.frame(nombre = c("Thomas", "Linda"))
is.factor(df$nombre)
#> [1] FALSE
df <- data.frame(nombre = c("Thomas", "Linda"), stringsAsFactors = TRUE)
is.factor(df$nombre)
#> [1] TRUEListas
Una lista puede contener valores de distintos tipos, también vectores y otras listas.
miLista <- list(1, "Texto", c(3, 4))
miLista
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] "Texto"
#>
#> [[3]]
#> [1] 3 4Nombrar y leer elementos
Los corchetes simples [ ] devuelven de nuevo una lista. Los corchetes dobles [[ ]] y $ devuelven el propio elemento.
persona <- list(nombre = "Linda", edad = 34, aficiones = c("Bailar", "Leer"))
names(persona)
#> [1] "nombre" "edad" "aficiones"
persona$nombre
#> [1] "Linda"
persona[["aficiones"]]
#> [1] "Bailar" "Leer"
persona["edad"]
#> $edad
#> [1] 34
str(persona)
#> List of 3
#> $ nombre : chr "Linda"
#> $ edad : num 34
#> $ aficiones: chr [1:2] "Bailar" "Leer"Cambiar, añadir y eliminar elementos
persona$edad <- 35 # sobrescribir un elemento
persona$ciudad <- "Colonia" # añadir un elemento
persona$aficiones <- NULL # eliminar un elemento
names(persona)
#> [1] "nombre" "edad" "ciudad"Unir y convertir listas
a <- list(1, 2, 3)
b <- list(4, 5, 6)
miNuevaLista <- c(a, b)
unlist(miNuevaLista) # convertir una lista en un vector
#> [1] 1 2 3 4 5 6Matrices
Una matriz tiene siempre dos dimensiones y valores de un único tipo. Sin byrow = TRUE, R rellena la matriz por columnas.
matrix(1:6, nrow = 2, ncol = 3)
#> [,1] [,2] [,3]
#> [1,] 1 3 5
#> [2,] 2 4 6
matrix(1:6, nrow = 2, ncol = 3, byrow = TRUE)
#> [,1] [,2] [,3]
#> [1,] 1 2 3
#> [2,] 4 5 6Nombrar filas y columnas
myMatrix <- matrix(1:12, nrow = 3, ncol = 4, byrow = TRUE,
dimnames = list(c("row1", "row2", "row3"),
c("col1", "col2", "col3", "col4")))
myMatrix
#> col1 col2 col3 col4
#> row1 1 2 3 4
#> row2 5 6 7 8
#> row3 9 10 11 12
dim(myMatrix)
#> [1] 3 4Acceder a filas, columnas y valores
myMatrix[1, ] # primera fila
#> col1 col2 col3 col4
#> 1 2 3 4
myMatrix[, 1] # primera columna
#> row1 row2 row3
#> 1 5 9
myMatrix[1, 3] # valor de la fila 1, columna 3
#> [1] 3
myMatrix["row2", "col4"]
#> [1] 8Calcular con matrices
+, -, * y / calculan elemento a elemento. %*% es la multiplicación de matrices.
m1 <- matrix(1:6, nrow = 2, byrow = TRUE)
m2 <- matrix(1:6, nrow = 2, byrow = TRUE)
m1 + m2
#> [,1] [,2] [,3]
#> [1,] 2 4 6
#> [2,] 8 10 12
t(m1) # intercambiar filas y columnas
#> [,1] [,2]
#> [1,] 1 4
#> [2,] 2 5
#> [3,] 3 6
m1 %*% t(m2)
#> [,1] [,2]
#> [1,] 14 32
#> [2,] 32 77
rowSums(m1)
#> [1] 6 15
colMeans(m1)
#> [1] 2.5 3.5 4.5Añadir filas y columnas
m <- matrix(1:4, nrow = 2)
rbind(m, c(9, 9)) # añadir una fila
#> [,1] [,2]
#> [1,] 1 3
#> [2,] 2 4
#> [3,] 9 9
cbind(m, c(9, 9)) # añadir una columna
#> [,1] [,2] [,3]
#> [1,] 1 3 9
#> [2,] 2 4 9Arrays
Un array puede tener cualquier número de dimensiones. El siguiente array tiene 3 filas, 4 columnas y 2 capas.
array("a", dim = c(3, 4, 2))
#> , , 1
#>
#> [,1] [,2] [,3] [,4]
#> [1,] "a" "a" "a" "a"
#> [2,] "a" "a" "a" "a"
#> [3,] "a" "a" "a" "a"
#>
#> , , 2
#>
#> [,1] [,2] [,3] [,4]
#> [1,] "a" "a" "a" "a"
#> [2,] "a" "a" "a" "a"
#> [3,] "a" "a" "a" "a"Array con nombres
Si no hay valores suficientes para todas las celdas, R los repite. Aquí los números del 1 al 12 rellenan las dos capas.
myArray <- array(1:12, dim = c(3, 4, 2),
dimnames = list(c("row1", "row2", "row3"),
c("col1", "col2", "col3", "col4")))
myArray[1, 3, 2] # fila 1, columna 3, capa 2
#> [1] 7
myArray[, , 2] # toda la segunda capa
#> col1 col2 col3 col4
#> row1 1 4 7 10
#> row2 2 5 8 11
#> row3 3 6 9 12Sobrescribir y analizar una capa
apply() aplica una función a las filas (1), a las columnas (2) o a ambas (c(1, 2)). El ejemplo calcula la suma de cada fila sobre todas las columnas y las dos capas.
myArray[, , 2] <- 13:24
apply(myArray, 1, sum)
#> row1 row2 row3
#> 92 100 108Data frames
Un data frame es una tabla. Todos los valores de una columna tienen el mismo tipo y todas las columnas tienen la misma longitud.
myDataFrame <- data.frame(
nombre = c("Thomas", "Johan", "Linda"),
edad = c(34, 21, 49),
sexo = c("H", "H", "M")
)
myDataFrame
#> nombre edad sexo
#> 1 Thomas 34 H
#> 2 Johan 21 H
#> 3 Linda 49 MMostrar la estructura
str(myDataFrame)
#> 'data.frame': 3 obs. of 3 variables:
#> $ nombre: chr "Thomas" "Johan" "Linda"
#> $ edad : num 34 21 49
#> $ sexo : chr "H" "H" "M"
summary(myDataFrame)
#> nombre edad sexo
#> Length:3 Min. :21.00 Length:3
#> Class :character 1st Qu.:27.50 Class :character
#> Mode :character Median :34.00 Mode :character
#> Mean :34.67
#> 3rd Qu.:41.50
#> Max. :49.00
dim(myDataFrame) # filas y columnas
#> [1] 3 3
nrow(myDataFrame)
#> [1] 3
colnames(myDataFrame)
#> [1] "nombre" "edad" "sexo"
head(myDataFrame, 2) # las primeras filas
#> nombre edad sexo
#> 1 Thomas 34 H
#> 2 Johan 21 HEn RStudio, View(myDataFrame) abre la tabla en una ventana aparte. edit() y fix() abren un editor sencillo en el que se pueden cambiar los valores directamente.
View(myDataFrame)
myDataFrame <- edit(myDataFrame)
fix(myDataFrame) # guarda el cambio directamente en myDataFrameLeer columnas
myDataFrame$nombre # columna como vector
#> [1] "Thomas" "Johan" "Linda"
myDataFrame[["edad"]] # lo mismo con el nombre como texto
#> [1] 34 21 49
myDataFrame[, c("nombre", "edad")] # varias columnas como data frame
#> nombre edad
#> 1 Thomas 34
#> 2 Johan 21
#> 3 Linda 49
myDataFrame[c(1, 3)] # columnas 1 y 3
#> nombre sexo
#> 1 Thomas H
#> 2 Johan H
#> 3 Linda MLeer y filtrar filas
myDataFrame[1, ] # primera fila
#> nombre edad sexo
#> 1 Thomas 34 H
myDataFrame[myDataFrame$edad > 30, ] # filas con una condición
#> nombre edad sexo
#> 1 Thomas 34 H
#> 3 Linda 49 M
subset(myDataFrame, edad > 30, select = c(nombre, edad))
#> nombre edad
#> 1 Thomas 34
#> 3 Linda 49Añadir columnas y filas
myDataFrame$ciudad <- c("Colonia", "Bonn", "Essen") # nueva columna
nuevasFilas <- data.frame(
nombre = c("Igor", "Andreas"),
edad = c(19, 25),
sexo = c("H", "H"),
ciudad = c("Bonn", "Colonia")
)
myDataFrame <- rbind(myDataFrame, nuevasFilas) # nuevas filas
myDataFrame
#> nombre edad sexo ciudad
#> 1 Thomas 34 H Colonia
#> 2 Johan 21 H Bonn
#> 3 Linda 49 M Essen
#> 4 Igor 19 H Bonn
#> 5 Andreas 25 H ColoniaOrdenar
myDataFrame[order(myDataFrame$edad), ]
#> nombre edad sexo ciudad
#> 4 Igor 19 H Bonn
#> 2 Johan 21 H Bonn
#> 5 Andreas 25 H Colonia
#> 1 Thomas 34 H Colonia
#> 3 Linda 49 M Essen
myDataFrame[order(-myDataFrame$edad), ] # descendente
#> nombre edad sexo ciudad
#> 3 Linda 49 M Essen
#> 1 Thomas 34 H Colonia
#> 5 Andreas 25 H Colonia
#> 2 Johan 21 H Bonn
#> 4 Igor 19 H BonnAgrupar y resumir
aggregate(edad ~ sexo, data = myDataFrame, FUN = mean)
#> sexo edad
#> 1 H 24.75
#> 2 M 49.00
table(myDataFrame$ciudad)
#>
#> Bonn Colonia Essen
#> 2 2 1Unir tablas
ciudades <- data.frame(ciudad = c("Colonia", "Bonn", "Essen"),
habitantes = c(1084831, 336465, 586608))
merge(myDataFrame, ciudades, by = "ciudad")
#> ciudad nombre edad sexo habitantes
#> 1 Bonn Johan 21 H 336465
#> 2 Bonn Igor 19 H 336465
#> 3 Colonia Thomas 34 H 1084831
#> 4 Colonia Andreas 25 H 1084831
#> 5 Essen Linda 49 M 586608Condiciones
if y else
x <- 7
if (x > 10) {
"grande"
} else if (x > 5) {
"mediano"
} else {
"pequeño"
}
#> [1] "mediano"ifelse para vectores completos
if comprueba exactamente un valor. ifelse() comprueba cada valor de un vector.
numeros <- c(3, 8, 12)
ifelse(numeros > 5, "grande", "pequeño")
#> [1] "pequeño" "grande" "grande"switch
dia <- "sa"
switch(dia,
sa = ,
do = "fin de semana",
"laborable")
#> [1] "fin de semana"Varios comandos en una línea se separan con punto y coma:
"hello"; 1 + 1; 1 * 3
#> [1] "hello"
#> [1] 2
#> [1] 3Bucles
for
for (i in seq(from = 0, to = 20, by = 5)) {
print(i)
}
#> [1] 0
#> [1] 5
#> [1] 10
#> [1] 15
#> [1] 20seq_along() devuelve las posiciones de un vector y funciona también con vectores vacíos:
personas <- c("Linda", "Sam")
for (i in seq_along(personas)) {
cat(i, personas[i], "\n")
}
#> 1 Linda
#> 2 Samwhile y repeat
i <- 5
while (i <= 15) {
print(i)
i <- i + 5
}
#> [1] 5
#> [1] 10
#> [1] 15
i <- 5
repeat {
if (i > 15) break # salir del bucle
print(i)
i <- i + 5
}
#> [1] 5
#> [1] 10
#> [1] 15next
for (i in 1:6) {
if (i %% 2 == 0) next # saltar los números pares
print(i)
}
#> [1] 1
#> [1] 3
#> [1] 5Muchos bucles se pueden escribir en R de forma más corta sin bucle, porque comandos como seq(), sum() o + actúan sobre vectores completos:
seq(0, 20, by = 5)
#> [1] 0 5 10 15 20Funciones
Llamar a una función
Los parámetros se pueden pasar por posición o por nombre.
log2(64)
#> [1] 6
log(x = 64, base = 2)
#> [1] 6
log(64, 2)
#> [1] 6Escribir una función propia
Una función devuelve el resultado de su última línea. Con return() se puede salir antes.
cuadrado <- function(x) x^2
cuadrado(4)
#> [1] 16
saludar <- function(nombre, saludo = "Hola") {
if (nombre == "") return("No se indicó ningún nombre")
paste(saludo, nombre)
}
saludar("Linda") # valor por defecto para saludo
#> [1] "Hola Linda"
saludar("Linda", saludo = "Buenas") # sobrescribir el valor por defecto
#> [1] "Buenas Linda"
saludar("")
#> [1] "No se indicó ningún nombre"Notación abreviada
Desde R 4.1 se puede escribir \(x) en lugar de function(x).
duplicar <- \(x) x * 2
duplicar(21)
#> [1] 42Cualquier número de parámetros
suma <- function(...) sum(...)
suma(1, 2, 3, 4)
#> [1] 10Aplicar una función a muchos valores
sapply() devuelve un vector, lapply() una lista. vapply() comprueba además el tipo del resultado. Map() recorre varios vectores a la vez.
sapply(1:5, \(x) x^2)
#> [1] 1 4 9 16 25
lapply(1:2, \(x) x * 10)
#> [[1]]
#> [1] 10
#>
#> [[2]]
#> [1] 20
vapply(c("a", "bb", "ccc"), nchar, integer(1))
#> a bb ccc
#> 1 2 3
Map(\(x, y) x + y, 1:3, 4:6) |> unlist()
#> [1] 5 7 9Gestión de errores
resultado <- tryCatch(
log(-1),
warning = function(w) "aviso capturado",
error = function(e) "error capturado"
)
resultado
#> [1] "aviso capturado"
comprobar <- function(x) {
if (!is.numeric(x)) stop("x debe ser un número")
x * 2
}
tryCatch(comprobar("a"), error = function(e) conditionMessage(e))
#> [1] "x debe ser un número"
stopifnot(is.numeric(5), 5 > 0) # se detiene si una condición no se cumpleMatemáticas y estadística
Redondeo y funciones básicas
round(sqrt(99))
#> [1] 10
round(sqrt(99), digits = 2)
#> [1] 9.95
signif(123456, 2) # a 2 cifras significativas
#> [1] 120000
ceiling(2.1) # redondear hacia arriba
#> [1] 3
floor(2.9) # redondear hacia abajo
#> [1] 2
abs(-3)
#> [1] 3
exp(1)
#> [1] 2.718282
10 %% 3
#> [1] 1Cifras mostradas
Por defecto R muestra los números con 7 cifras significativas. options(digits = ) admite valores de 1 a 22. El ajuste solo cambia la presentación, no la precisión del cálculo.
pi
#> [1] 3.141593
options(digits = 15)
pi
#> [1] 3.14159265358979
options(digits = 7)Posición y dispersión
x <- c(1, 2, 4, 99)
mean(x) # media
#> [1] 26.5
median(x) # mediana
#> [1] 3
min(x); max(x)
#> [1] 1
#> [1] 99
range(x)
#> [1] 1 99
diff(range(x)) # recorrido
#> [1] 98
sum(x)
#> [1] 106
var(x) # varianza
#> [1] 2337.667
sd(x) # desviación típica
#> [1] 48.34942
quantile(x)
#> 0% 25% 50% 75% 100%
#> 1.00 1.75 3.00 27.75 99.00
summary(x)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 1.00 1.75 3.00 26.50 27.75 99.00Relación entre dos valores
cor(mtcars$mpg, mtcars$wt) # correlación
#> [1] -0.8676594
modelo <- lm(mpg ~ wt, data = mtcars) # regresión lineal
coef(modelo)
#> (Intercept) wt
#> 37.285126 -5.344472Números aleatorios
Con set.seed(), R devuelve los mismos números aleatorios en cada ejecución. Así los resultados se pueden reproducir.
set.seed(42)
sample(1:1000, 5) # 5 números sin repetición
#> [1] 561 997 321 153 74
sample(c("cara", "cruz"), 3, replace = TRUE)
#> [1] "cruz" "cruz" "cruz"
round(runif(3), 2) # distribución uniforme entre 0 y 1
#> [1] 0.66 0.71 0.46
round(rnorm(3, mean = 100, sd = 15), 1) # distribución normal
#> [1] 108.7 90.1 123.3Dar formato a números
format(20, nsmall = 2) # al menos 2 decimales
#> [1] "20.00"
format(20.123456789, digits = 4) # 4 cifras significativas
#> [1] "20.12"
format(20, scientific = TRUE) # notación científica
#> [1] "2e+01"
format(1234567.891, big.mark = ".", decimal.mark = ",", nsmall = 2)
#> [1] "1.234.567,89"
format(100) # número como texto
#> [1] "100"Fecha y hora
Sys.Date() # fecha de hoy
Sys.time() # hora actuald <- as.Date("18/10/2016", format = "%d/%m/%Y")
d
#> [1] "2016-10-18"
format(d, "%d/%m/%Y")
#> [1] "18/10/2016"
format(d, "%Y") # solo el año
#> [1] "2016"
d + 30 # 30 días después
#> [1] "2016-11-17"
as.numeric(as.Date("2026-10-29") - d) # diferencia en días
#> [1] 3663
seq(d, by = "month", length.out = 3)
#> [1] "2016-10-18" "2016-11-18" "2016-12-18"Gráficos
R base dibuja gráficos sin paquetes adicionales:
plot(mtcars$wt, mtcars$mpg, xlab = "Peso", ylab = "Millas por galón")
hist(mtcars$mpg, main = "Distribución", col = "steelblue")
barplot(table(mtcars$cyl))
boxplot(mpg ~ cyl, data = mtcars)El paquete ggplot2 construye los gráficos por capas:
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg, colour = factor(cyl))) +
geom_point() +
labs(x = "Peso", y = "Millas por galón", colour = "Cilindros")
ggsave("grafico.png", width = 6, height = 4)Otros paquetes para gráficos son lattice y plotly.
Manipular datos con dplyr
El paquete dplyr ofrece comandos sencillos para filtrar, seleccionar, calcular y resumir. El pipe |> pasa el resultado al siguiente comando.
library(dplyr)
iris |>
filter(Sepal.Length > 5) |> # filtrar filas
mutate(Largo_Ancho = Sepal.Length / Sepal.Width) |> # calcular una columna
group_by(Species) |> # agrupar
summarise(Cantidad = n(),
Media = mean(Largo_Ancho)) |> # resumir por grupo
arrange(desc(Media)) # ordenar de forma descendente
#> # A tibble: 3 × 3
#> Species Cantidad Media
#> <fct> <int> <dbl>
#> 1 virginica 49 2.24
#> 2 versicolor 47 2.16
#> 3 setosa 22 1.44iris |>
select(Species, Petal.Length) |> # seleccionar columnas
slice_head(n = 3) # las primeras filas
#> Species Petal.Length
#> 1 setosa 1.4
#> 2 setosa 1.4
#> 3 setosa 1.3Bases de datos
El paquete DBI es la interfaz común para bases de datos en R. Cada base de datos tiene su propio controlador:
| Base de datos | Paquete |
|---|---|
| SQLite | RSQLite |
| MySQL y MariaDB | RMariaDB (sustituye al antiguo RMySQL) |
| PostgreSQL | RPostgres |
| cualquier base de datos con controlador ODBC | odbc (el antiguo RODBC sigue funcionando, pero no usa la interfaz DBI) |
install.packages(c("DBI", "RSQLite"))El siguiente ejemplo crea una base de datos en memoria, escribe una tabla en ella y la consulta con SQL:
library(DBI)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "coches", mtcars[1:5, c("mpg", "cyl", "hp")])
dbListTables(con)
#> [1] "coches"
dbGetQuery(con, "SELECT cyl, AVG(hp) AS cv FROM coches GROUP BY cyl")
#> cyl cv
#> 1 4 93
#> 2 6 110
#> 3 8 175
dbDisconnect(con)Conexión a un servidor MariaDB o MySQL:
con <- dbConnect(RMariaDB::MariaDB(),
host = "localhost", dbname = "tienda",
user = "aaron", password = Sys.getenv("DB_PASSWORD"))La contraseña no debe estar en el código. Sys.getenv() la lee de una variable de entorno, que se puede definir por ejemplo en el archivo .Renviron.
Entropía y ganancia de información
La entropía mide lo mezclados que están los valores de una columna objetivo. La ganancia de información (information gain) indica cuánto reduce esa mezcla una característica. Ambos valores ayudan a los árboles de decisión a encontrar la característica con mayor poder predictivo.
entropia <- function(y) {
p <- table(y) / length(y)
p <- p[p > 0]
-sum(p * log2(p))
}
ganancia_informacion <- function(y, x) {
condicional <- sum(sapply(split(y, x), \(parte) length(parte) / length(y) * entropia(parte)))
entropia(y) - condicional
}
entropia(iris$Species)
#> [1] 1.584963
ganancia_informacion(iris$Species, cut(iris$Petal.Length, 3))
#> [1] 1.324664
ganancia_informacion(iris$Species, cut(iris$Sepal.Width, 3))
#> [1] 0.2658956La longitud del pétalo (Petal.Length) separa las especies mucho mejor que la anchura del sépalo (Sepal.Width). El paquete FSelector ofrece la función information.gain() para todas las columnas a la vez, pero necesita una instalación de Java.
