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 comando

Salida 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
#> Listo

Ayuda 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 comando

Historial de comandos y salir de R

history()           # mostrar los últimos comandos introducidos
q()                 # salir de R, en RStudio termina la sesión

Versión y sesión

R.version.string    # versión de R instalada
sessionInfo()       # versión, sistema operativo y paquetes cargados

Directorio 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 trabajo

Archivos 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 archivo

Leer 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 paquete

Una 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 paquete

Mostrar 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 paquete

Cargar 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 5

Mostrar 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 variables

Comparar 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] TRUE

Tipos 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

EstructuraContenidoSe crea con
Vectorvalores del mismo tipo en filac()
Listavalores de cualquier tipo, también otras listaslist()
Matrizvalores del mismo tipo en filas y columnasmatrix()
Arrayvalores del mismo tipo en cualquier número de dimensionesarray()
Factorcategorías con valores fijosfactor()
Data frametabla, cada columna tiene su propio tipodata.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] NA

Valores 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] 0

Operadores

Operadores aritméticos

OperadorSignificadoEjemploResultado
+ - * /operaciones básicas7 / 23.5
^potencia2 ^ 38
%%resto de una división (módulo)7 %% 21
%/%división entera7 %/% 23

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 16

Operadores 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 FALSE

Operadores 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] FALSE

Operadores de asignación

OperadorSignificado
<- 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] 2

Otros 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] 6

Vectores

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 13

Ordenar 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 46

Unir 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      3

Orden 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  TRUE

Crear 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] TRUE

Listas

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 4

Nombrar 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 6

Matrices

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    6

Nombrar 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 4

Acceder 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] 8

Calcular 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.5

Añ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    9

Arrays

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   12

Sobrescribir 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  108

Data 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    M

Mostrar 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    H

En 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 myDataFrame

Leer 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    M

Leer 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   49

Añ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 Colonia

Ordenar

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    Bonn

Agrupar 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       1

Unir 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     586608

Condiciones

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] 3

Bucles

for

for (i in seq(from = 0, to = 20, by = 5)) {
  print(i)
}
#> [1] 0
#> [1] 5
#> [1] 10
#> [1] 15
#> [1] 20

seq_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 Sam

while 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] 15

next

for (i in 1:6) {
  if (i %% 2 == 0) next   # saltar los números pares
  print(i)
}
#> [1] 1
#> [1] 3
#> [1] 5

Muchos 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 20

Funciones

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] 6

Escribir 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] 42

Cualquier número de parámetros

suma <- function(...) sum(...)
suma(1, 2, 3, 4)
#> [1] 10

Aplicar 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 9

Gestió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 cumple

Matemá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] 1

Cifras 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.00

Relació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.344472

Nú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.3

Dar 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 actual
d <- 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.44
iris |>
  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.3

Bases 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 datosPaquete
SQLiteRSQLite
MySQL y MariaDBRMariaDB (sustituye al antiguo RMySQL)
PostgreSQLRPostgres
cualquier base de datos con controlador ODBCodbc (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.2658956

La 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.