Leer datos del mundo real con readr

r
tidyverse
read_csv() vs read.csv(), control de tipos por columna, locale para datos en formato europeo (decimales con coma, fechas dd/mm/yyyy).

Por qué readr y no R base

R base tiene read.csv() desde siempre. El tidyverse incluye read_csv() de readr. La diferencia no es marketing, son comportamientos por defecto distintos que importan:

read.csv() (base) read_csv() (readr)
Velocidad Lenta ~10× más rápida
Strings Convierte a factor por defecto (sorpresa clásica antes de R 4.0) Las mantiene como character
Tipo de retorno data.frame tibble (impresión más limpia)
Mensajes Silencioso (no avisa de problemas) Reporta tipos detectados y problemas
na.strings Por defecto solo "" y NA Reconoce más representaciones (NA, "", etc.)

Usa read_csv() por defecto. La excepción es data.table::fread() cuando trabajas con archivos enormes (varios GB) donde la velocidad de lectura es el cuello de botella.

read_csv(), read_csv2(), read_delim()

library(readr)

# CSV separado por comas (estándar internacional)
ventas <- read_csv("data/ventas.csv")

# CSV separado por punto-y-coma (común en Europa)
ventas <- read_csv2("data/ventas.csv")

# Cualquier otro delimitador (tab, pipe, etc.)
log <- read_delim("data/log.txt", delim = "|")

# TSV (tab-separated)
genes <- read_tsv("data/genes.tsv")

read_csv2 importa más de lo que parece en datos europeos: por convención, en países donde la coma es el separador decimal (España, Francia, Alemania), los CSV usan punto-y-coma como separador de columnas para evitar ambigüedad. Si abres el archivo en un editor de texto y ves ;, usa read_csv2.

Lo que readr adivina (y a veces se equivoca)

Cuando ejecutas read_csv("data/ventas.csv"), verás un mensaje como:

Rows: 10000 Columns: 5
── Column specification ─────────────────────────
Delimiter: ","
chr  (2): region, vendedor
dbl  (2): unidades, precio_unidad
date (1): fecha

readr ha leído las primeras filas (por defecto 1000) y ha deducido el tipo de cada columna. Esto es muy útil pero no es magia. Tres puntos a tener claros:

  1. Es una estimación, no una verdad. Si las primeras 1000 filas de una columna parecen números pero la fila 1500 tiene texto, readr la tipará como dbl y ese texto se convertirá en NA con un warning. Léelos.
  2. Las fechas se detectan solo en formato ISO (yyyy-mm-dd). Si tu archivo tiene 01/03/2024, readr lo dejará como character. Hay que decírselo.
  3. El mensaje de tipos lo puedes silenciar con show_col_types = FALSE, pero al principio es información valiosa. No lo silencies por reflejo.

Tipos por columna: cómo darle pistas

Cuando readr se equivoca o cuando quieres ser explícito, usa col_types:

ventas <- read_csv(
  "data/ventas.csv",
  col_types = cols(
    fecha          = col_date(format = "%d/%m/%Y"),
    region         = col_character(),
    vendedor       = col_character(),
    unidades       = col_integer(),
    precio_unidad  = col_double(),
    activo         = col_logical()
  )
)

Especificadores frecuentes:

  • col_character(), col_integer(), col_double(), col_logical(), los básicos.
  • col_factor(levels = c("A", "B")): factor con niveles controlados.
  • col_date(format = "%d/%m/%Y"): fechas con formato custom.
  • col_datetime(format = "..."): fechas con hora.
  • col_skip(): ignorar la columna (útil cuando hay columnas basura).

Atajo cuando hay muchas columnas y solo quieres especificar algunas:

ventas <- read_csv(
  "data/ventas.csv",
  col_types = cols(
    fecha    = col_date(format = "%d/%m/%Y"),
    .default = col_guess()
  )
)

.default = col_guess() deja que readr adivine el resto. Es el patrón sano: corrige lo que sabes que es problemático, deja el resto al detector automático.

El locale: cuando los decimales son comas

Datos generados en hojas de cálculo europeas suelen venir con:

  • Decimales con coma (1.234,56 para “mil doscientos treinta y cuatro coma cincuenta y seis”).
  • Separador de miles con punto.
  • Fechas en formato dd/mm/yyyy.

readr interpreta esto con un objeto locale():

ventas <- read_csv2(
  "data/ventas.csv",
  locale = locale(
    decimal_mark  = ",",
    grouping_mark = ".",
    date_format   = "%d/%m/%Y"
  )
)

Esto le dice: “los números pueden tener coma como decimal y punto como agrupador. Las fechas vienen en formato europeo”. Sin este locale(), los números como "1.234,56" se interpretan como string (porque tienen comas) y readr los deja como character.

Para datos en formato anglosajón (decimal con punto, fecha ISO), el locale() por defecto está bien y no necesitas tocarlo.

Trampas habituales

  • CSVs que pasaron por Excel. Excel a veces “corrige” datos al guardar: convierte códigos como "0123" en número (perdiendo el cero inicial), reinterpreta fechas según el locale del sistema, exporta números con coma decimal aunque originalmente fueran con punto. Antes de leer un CSV que pasó por Excel, ábrelo en un editor de texto plano (VS Code, Notepad++) y comprueba qué hay realmente dentro.
  • Encoding raro. Si tienes acentos que se ven como ñ en vez de ñ, el archivo está en UTF-8 pero R lo lee como latin1 (o al revés). read_csv("file.csv", locale = locale(encoding = "latin1")) resuelve la mayoría de los casos.
  • El BOM al principio del archivo. Algunos CSV UTF-8 generados en Windows empiezan con un BOM (byte-order-mark, tres bytes invisibles). Hace que la primera columna se llame algo como "region" en vez de "region". readr desde la versión 2.0 maneja el BOM automáticamente, pero si ves nombres raros en la primera columna, ya sabes la causa.

En la siguiente entrega

Ya tienes los datos en un tibble. El primer verbo que aplicarás casi siempre es filter(): quedarte solo con las filas que te interesan. Es el siguiente tutorial, y tiene más profundidad de la que parece, especialmente con los NA.