Capítulo18 Importar datos

Fecha de la última revisión

## [1] "2026-09-09"

18.1 Temas

  • De dónde salen los datos: la web, un archivo tuyo, un paquete
  • read_csv() y su familia, y en qué se diferencia de read.csv()
  • Los argumentos que de verdad se usan: skip, na, col_names, col_types
  • Cuando la columna llega con el tipo equivocado: las funciones parse_*()
  • Acentos y codificación
  • Escribir un archivo: write_csv()
  • Otros formatos: Excel, SPSS, bases de datos

18.2 De dónde salen los datos

Hasta ahora todos los datos venían de un paquete: escribías library(datos) y vuelos aparecía. Eso es cómodo para aprender y no se parece en nada a la vida real. En un proyecto de verdad los datos llegan de tres sitios:

  1. De internet, con una dirección que apunta a un archivo .csv. Es rápido y siempre está al día, pero depende de que haya conexión y de que quien publica no cambie el archivo debajo de ti.
  2. De un archivo tuyo, dentro de la carpeta del proyecto. Es lo más común y lo más reproducible.
  3. De un paquete, como hemos hecho en clase.

La regla práctica que conviene adoptar desde ahora: baja el archivo una vez, guárdalo en Datos/, y de ahí en adelante trabaja con tu copia. Así tu análisis corre igual dentro de un año, sin internet y aunque la fuente original haya desaparecido.

Importar es el paso donde se cuelan la mitad de los errores de un análisis, y son errores silenciosos: una columna que llega como texto, un decimal leído como separador de miles, un acento convertido en símbolo raro. Después de cada importación, mira la tabla antes de calcular nada.


18.3 Datos importados de la web.

Los ejemplos de esta sección descargan datos en vivo desde internet con fread("https://…"), así que necesitas conexión para ejecutarlos. Si ves Error in download.file(...): cannot open URL o un HTTP error, revisa tu conexión o inténtalo más tarde: el servidor puede estar caído o tardar en responder. Una buena práctica es descargar el archivo una vez y guardarlo con write_csv() en Datos/ para no depender de la red. Encontrarás más errores frecuentes y cómo resolverlos en el capítulo Errores comunes (14).

18.3.1 Usar datos que provienen de una base de datos del web.

Aquí un website que recopila la cantidad de personas vacunadas en los EEUU.

library(tidyverse)
library(data.table)

Seleccionar solamente los datos de Puerto Rico

18.3.2 COVID world

Otro website de datos de COVID, estos representan los datos de todos los países del mundo


Para encontrar datos de PR vea el siguiente enlace: Este sitio del gobierno de Puerto Rico almacena datos muchos diferentes tipos que están relacionados a la isla.

https://datos.estadisticas.pr/dataset?res_format=CSV

Nota que para leer los datos de un archivo CSV, se usa la función “fread()” del paquete “data.table”.

  • Cuando entre a la página, selecciona uno de los archivos, por ejemplo Facultad -Conservatorio de Musica
  • Presione el botón “Explore” y selecciona “Preview”
  • Arriba de la página verá el enlace de https://……… para poner en un script.
  • Copie y pegue en el script.

Otra alternativa es bajar los datos en formato .csv o Excel y subirlo a su proyecto en RStudio.

18.4 Ejercicio

  • Selecciona solamente los datos de los bebés que nacieron primer mes del año, pero agrupado por el día de la semana y contabilizado

COVID_PR

  • Calcula la cantidad de muertes por día en PR, y el máximo de muerto en un día por COVID

18.5 Datos importando de su computadora

Esta es la manera que vas a usar el 90 % de las veces: el archivo está dentro de la carpeta del proyecto y se lee con una ruta relativa, "Datos/archivo.csv".

18.5.1 Temas: Funciones para importar datos con el paquete readr

Cada función de la lista corresponde a un formato de archivo distinto. Lo único que cambia entre ellas es qué separa una columna de la siguiente:

  • read_csv() # separado por coma
  • read_csv2() # separado por punto y coma
  • read_tsv() # separado por tab
  • read_delim() # separado por delimitador
  • read_fwf()
  • read_width()
  • read_positions()
  • read_table()

read_csv() (readr) y fread() (data.table): importar datos.

  • read_csv("archivo.csv"): lee un CSV a un tibble; variantes: read_csv2() (separado por ;), read_tsv() (tabs), read_delim() (delimitador a elección).
  • fread("https://…"): rápido y práctico para leer directamente desde una URL.
  • write_csv(objeto, "archivo.csv"): guarda tus datos para no depender de la red.

18.5.2 read_csv() frente a read.csv()

Se parecen tanto que es fácil confundirlas, y no son lo mismo. read.csv() es la de base R, con punto; read_csv() es la de readr, con guion bajo. Prefiere siempre la segunda:

  • devuelve un tibble, así que ves los tipos de columna al imprimirla;
  • es bastante más rápida con archivos grandes;
  • no convierte el texto en factores ni cambia los nombres de las columnas (Area Policiaca sigue llamándose así, no Area.Policiaca);
  • te dice en un mensaje cómo interpretó cada columna, que es justo lo que hay que revisar.

18.5.3 Los argumentos que de verdad se usan

Argumentos de read_csv(): lo que hay que saber para archivos del mundo real.

  • skip = 2: salta las primeras líneas. Sirve cuando el archivo trae un título o un logo antes de la tabla.
  • col_names = FALSE: el archivo no tiene fila de nombres. También acepta un vector con los nombres que quieras ponerle.
  • na = c("", "NA", "n/a", "."): qué textos hay que tratar como valor faltante. Por defecto solo "" y "NA".
  • col_types = cols(codigo = col_character()): obliga a una columna a leerse con el tipo que tú digas. Es la solución cuando un código postal o un identificador que empieza con cero se lee como número y pierde el cero.
  • comment = "#": ignora las líneas que empiecen con ese símbolo.

read_csv() adivina el tipo de cada columna mirando las primeras mil filas. Si el problema aparece en la fila 5000 (una celda que dice “no medido” en una columna de números), la adivinanza sale mal. Cuando una columna llegue con el tipo equivocado, no la arregles después con as.numeric(): dile a read_csv() cómo leerla desde el principio con col_types.

library(tidyverse)
library(readr)
Newborn_Karn_Penrose <- read_csv("Datos/Newborn_Karn_Penrose.csv")


#library(readxl)
#Newborn_Karn_Penrose1 <- read_excel("~/Google Drive/GitHub_Google_Drive/GitHub/Vintage_DATA/DATA/Human_Birth_Weight/Newborn_Karn_Penrose.xlsx", sheet = 3)

head(Newborn_Karn_Penrose)
## # A tibble: 6 × 4
##   Mother_age Count_Non_survivors Count_Total_Birth Birth_weight
##        <dbl>               <dbl>             <dbl>        <dbl>
## 1         16                   0                 1          9.5
## 2         16                   0                 2          9  
## 3         16                   0                 1          8.5
## 4         16                   0                 3          8  
## 5         16                   0                 7          7.5
## 6         16                   0                 2          7

Estos datos vienen de un estudio clásico de peso al nacer: cada fila es una combinación de edad de la madre y peso del bebé, con cuántos nacimientos hubo y cuántos no sobrevivieron. Fíjate en que no es una fila por bebé: son conteos agrupados, y eso cambia cómo hay que leerlos.

Hacer una columna de la proporción de los niños que no sobrevivieron.

La versión larga, con las columnas seleccionadas y ordenadas:

Newborn_Karn_Penrose %>%
  dplyr::select(Count_Non_survivors, Count_Total_Birth) %>%
  arrange(Count_Total_Birth, Count_Non_survivors) %>%
  mutate(Perc_sobrevivieron = Count_Non_survivors/Count_Total_Birth)

Y la versión corta, que es la que corre aquí:

Newborn_Karn_Penrose %>% 
  mutate(Percent_Decease = Count_Non_survivors/Count_Total_Birth)
## # A tibble: 261 × 5
##    Mother_age Count_Non_survivors Count_Total_Birth Birth_weight Percent_Decease
##         <dbl>               <dbl>             <dbl>        <dbl>           <dbl>
##  1         16                   0                 1          9.5               0
##  2         16                   0                 2          9                 0
##  3         16                   0                 1          8.5               0
##  4         16                   0                 3          8                 0
##  5         16                   0                 7          7.5               0
##  6         16                   0                 2          7                 0
##  7         16                   0                 4          6.5               0
##  8         16                   0                 2          6                 0
##  9         16                   0                 2          5.5               0
## 10         16                   0                 1          5                 0
## # ℹ 251 more rows

Lo mismo, ahora en gráfica. Primero la versión larga:

Newborn_Karn_Penrose %>%
  dplyr::select(Count_Non_survivors, Count_Total_Birth, Birth_weight) %>%
  arrange(Count_Total_Birth, Count_Non_survivors) %>%
  mutate(porc_sobrevivieron = Count_Non_survivors / Count_Total_Birth) %>%
  ggplot(aes(x = Birth_weight, y = porc_sobrevivieron)) +
  geom_point() +
  geom_smooth()

Y la corta, que es la que se ejecuta:

Newborn_Karn_Penrose %>% 
  mutate(Percent_Decease = Count_Non_survivors/Count_Total_Birth) %>% 
  ggplot(aes(Birth_weight, Percent_Decease)) + 
  geom_point() +
  geom_smooth()

Newborn_Karn_Penrose %>% 
  mutate(Percent_Decease = Count_Non_survivors/Count_Total_Birth) %>% 
  ggplot(aes(Birth_weight, Percent_Decease)) + 
  geom_point() +
  geom_smooth()+
  facet_wrap(~Mother_age)

Newborn_Karn_Penrose %>% 
  mutate(Percent_Decease = Count_Non_survivors/Count_Total_Birth) %>% 
  ggplot(aes(Birth_weight, Percent_Decease, color=Mother_age)) + 
  geom_point(aes(color=factor(Mother_age))) +
  geom_smooth(se=FALSE, aes(color=factor(Mother_age)))


18.6 Comparar con base R

read.csv() (base R) read_csv() (readr)
Qué devuelve un data.frame un tibble
Nombres de columna los “arregla”: Area Policiaca pasa a Area.Policiaca los deja tal cual
Texto en versiones viejas lo convertía en factor siempre texto
Velocidad lenta con archivos grandes mucho más rápida
Informe ninguno dice cómo interpretó cada columna

En resumen: read.csv() funciona, pero toma decisiones por su cuenta y no te avisa. read_csv() hace menos y te cuenta lo que hizo, que es lo que uno quiere cuando los datos son de verdad.


18.7 Segmentar un vector

Cuando read_csv() no acierta con el tipo de una columna, o cuando los datos llegan de otra parte, hay que convertir el texto a mano. De eso se encargan las funciones parse_*(): cada una toma un vector de texto y devuelve un vector del tipo que dice su nombre.

Son también las funciones que read_csv() usa por dentro, así que entenderlas explica por qué a veces adivina mal.

parse_*() (readr): convertir texto al tipo correcto.

  • Qué hacen: parse_integer(), parse_double(), parse_number(), parse_logical(), parse_date(), parse_factor() convierten cadenas de texto a números, fechas, etc.
  • parse_number("$123.45"): extrae el número ignorando símbolos como $ o %.
  • locale = locale(...): controla la codificación, el separador de miles y el formato de fecha.
  • str(parse_logical())

En el bloque siguiente, fíjate en el contraste: parse_logical() funciona con x, que contiene textos que R reconoce como lógicos, y falla con y, que contiene nombres de personas. Y fíjate sobre todo en mean(z): z parece numérico pero está entre comillas, así que es texto, y el promedio no se puede calcular.

x= c("TRUE", "FALSE", "NA")
x   
## [1] "TRUE"  "FALSE" "NA"
str(parse_logical(x)) 
##  logi [1:3] TRUE FALSE NA
y= c("Carlos", "Kelvin")
     
y
## [1] "Carlos" "Kelvin"
str(parse_logical(y)) 
##  logi [1:2] NA NA
##  - attr(*, "problems")= tibble [2 × 4] (S3: tbl_df/tbl/data.frame)
##   ..$ row     : int [1:2] 1 2
##   ..$ col     : int [1:2] NA NA
##   ..$ expected: chr [1:2] "1/0/T/F/TRUE/FALSE" "1/0/T/F/TRUE/FALSE"
##   ..$ actual  : chr [1:2] "Carlos" "Kelvin"
str(parse_logical(c("TRUE", "FALSE", "NA")))
##  logi [1:3] TRUE FALSE NA
z=c("1", "2", "3")
z
## [1] "1" "2" "3"
mean(z)
## [1] NA
z1=str(parse_integer(z))
##  int [1:3] 1 2 3
str(parse_integer(c("1", "2", "3")))
##  int [1:3] 1 2 3
str(parse_date(c("2010-01-01", "1979-10-14")))
##  Date[1:2], format: "2010-01-01" "1979-10-14"

parse_number() es la más útil de todas en la práctica: se queda con el número y tira todo lo demás. Con ella se limpian de un golpe las columnas de precios, de porcentajes y de cantidades escritas con símbolos.

m=c("$4000", "$500" )
parse_number(m)
## [1] 4000  500
parse_number("$100")
## [1] 100
#> [1] 100
parse_number("20%")
## [1] 20
#> [1] 20
parse_number("It cost $123.45")
## [1] 123.45
#> [1] 123.45
  • str(parse_integer())
  • str(parse_date())

18.8 Números

Con los números hay dos trampas, y las dos son cuestión de país.

La primera es el separador decimal: en inglés se escribe 1.5 y en español 1,5. La segunda es el separador de miles: en inglés 1,000,000 y en español 1.000.000. Es decir, el mismo signo significa cosas contrarias según de dónde venga el archivo.

Se resuelven con locale(), que le dice a readr en qué convención está escrito el archivo:

  • parse_double()
  • parse_number()
  • parse_number(locale=locale(grouping_mark = “.”))
parse_double("1,5", locale = locale(decimal_mark = ","))
parse_number("1.000.000", locale = locale(grouping_mark = "."))

Este error es traicionero porque no da ningún mensaje: si un archivo en español se lee con la convención inglesa, 1,5 se convierte en 15 y el análisis sigue con números diez veces más grandes. Cuando importes datos de una agencia de gobierno, mira siempre un par de valores y compáralos con el archivo original.


18.9 Cadena de texto

Aquí aparece el problema de los acentos. Una computadora no guarda letras, guarda números, y la tabla que traduce números a letras se llama codificación. Hoy casi todo usa UTF-8, que tiene sitio para la ñ, los acentos y los signos de apertura. Los archivos viejos, sobre todo los que salieron de un Excel en Windows, suelen venir en Latin1.

Si abres un archivo con la codificación equivocada, la ñ se convierte en símbolos raros. charToRaw() enseña los números que hay detrás de un texto, y guess_encoding() intenta adivinar en qué codificación está escrito.

  • parse_character()

  • charToRaw()

charToRaw("Ramn")
## [1] 52 61 6d 6e
x1 <- "El Ni\xf1o was particularly bad this year"
x2 <- "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"
x3 <- "The boy was particularly bad this year"

library(readr)
  parse_character(x1, locale = locale(encoding = "Latin1"))
## [1] "El Niño was particularly bad this year"
#> [1] "El Niño was particularly bad this year"
parse_character(x2, locale = locale(encoding = "Shift-JIS"))
## [1] "こんにちは"
parse_character(x2, locale = locale(encoding = "Shift-JIS"))
## [1] "こんにちは"
#> [1] "こんにちは"

Cuando veas Ni�o o Ni√±o en una tabla recién importada, no es que los datos estén dañados: es la codificación. Prueba read_csv(archivo, locale = locale(encoding = "Latin1")), y si no sabes cuál es, pregúntaselo a guess_encoding().

  • guess_encoding(charToRaw())
guess_encoding(charToRaw(x3))
## # A tibble: 1 × 2
##   encoding confidence
##   <chr>         <dbl>
## 1 ASCII             1

18.10 Factores

parse_factor() convierte texto en factor, pero pidiéndote la lista de niveles que esperas. Eso es útil precisamente porque avisa cuando aparece un valor que no estaba en tu lista: una errata, un “Carolína” con acento entre mil “Carolina” sin él.

  • parse_factor()
parse_factor(c("alta", "baja", "media"), levels = c("alta", "baja"))
# avisa de que "media" no estaba en los niveles

18.11 Fechas, fechas-horas, horas

Una fecha leída como texto no sirve de nada: no se puede ordenar bien, no se le puede restar otra fecha, no se puede agrupar por mes. Convertirla es de los primeros pasos después de importar.

  • parse_datetime()

  • library(hms)

  • parse_time()

parse_date("2016-08-01")
parse_date("01/08/2016", format = "%d/%m/%Y")
parse_time("15:30:00")

01/08/2016 es el 1 de agosto para media humanidad y el 8 de enero para la otra mitad. Si no dices el format, R usa el suyo y puede acertar por casualidad durante los primeros doce días del mes. Este capítulo solo abre el tema: las fechas tienen su propio capítulo más adelante, con el paquete lubridate.

18.12 Segmentar un archivo

  • guess_parser

guess_parser() contesta la pregunta “¿qué tipo diría readr que es esto?”. Es la manera de entender por qué una columna llegó como texto cuando tú la querías numérica: casi siempre hay una celda con un valor raro escondida en el medio.

guess_parser(c("1", "2", "3"))
guess_parser(c("1", "2", "no medido"))

18.13 Escribir un Archivo

Guardar es tan importante como leer, y por dos razones distintas: para no depender de la red, y para dejarle a otra persona (o a un revisor) el resultado de tu limpieza sin que tenga que correr todo tu script.

Después de descargar datos de internet, guárdalos con write_csv() para trabajar sin depender de la conexión en la próxima sesión.

  • write_csv()
  • write_tsv()

Un .csv no guarda los tipos: al volver a leerlo, readr tiene que adivinar otra vez, y una columna que tú habías convertido en factor o en fecha vuelve a ser texto. Si necesitas conservar los tipos exactamente, guarda con write_rds() (formato propio de R) o con arrow::write_parquet(). Para compartir con gente que no usa R, el .csv sigue siendo la mejor opción.

18.14 Otros tipos de datos y paquetes

R lee casi cualquier formato, siempre con la misma idea: un paquete por familia de archivos.

  • library(haven) # SPSS, Stata y SAS
  • readxl() lee archivo Excel en formato .xls, xlsx
  • BDI lee archivo RMySQL y otros

Paquetes para otros formatos: cuál usar según de dónde vengan los datos.

  • readxl::read_excel("archivo.xlsx", sheet = 2): hojas de Excel. El argumento sheet acepta el número o el nombre de la hoja.
  • haven: archivos de SPSS (.sav), Stata (.dta) y SAS. Es el camino habitual cuando los datos vienen de una encuesta o de otra disciplina.
  • DBI y RMariaDB / RPostgres: bases de datos, cuando la tabla es demasiado grande para caber en la memoria.
  • googlesheets4: hojas de Google, útiles cuando varias personas están llenando la tabla a la vez.

Con un Excel, el problema casi nunca es leerlo: es que la hoja trae celdas combinadas, un título en la primera fila y notas al pie debajo de la tabla. Los argumentos skip y range de read_excel() resuelven la mayoría de esos casos.


18.15 Ejercicios del libro R4DS

Hacer los ejercicios de las siguientes secciones del libro en español:

  • sección 11.2.2 (leer archivos),
  • sección 11.3.5 (segmentar vectores).

18.16 Ejercicios del capítulo

Sobre los ejercicios de este capítulo. Cada ejercicio se entrega con tres cosas: el script, el resultado (que el bloque corra) y una explicación en palabras. Los ejercicios usan Datos/crimen_pr.csv, un archivo real del gobierno de Puerto Rico que está en el repositorio del curso, y archivos que vas a crear tú mismo. No usan Newborn_Karn_Penrose.csv, que es el del capítulo. La hoja completa para entregar está en Ejercicios/Ejercicios_Capitulo_18_importar.Rmd.

18.16.1 Ejercicio 1. Mirar antes de calcular

Lee Datos/crimen_pr.csv con read_csv(). Antes de tocar nada, mira qué llegó: imprime el tibble, corre glimpse() y spec(), y cuenta los valores faltantes de cada columna.

Verificación: la tabla debe tener 8870 filas y 7 columnas, y dos de las columnas deben traer 733 valores faltantes cada una.

En palabras: readr convirtió esos faltantes solo porque en el archivo estaban escritos como NA. ¿Qué habría pasado si el archivo los hubiera escrito como "sin dato"? ¿Con cuál argumento lo arreglarías?

# Escribe tu código aquí

18.16.2 Ejercicio 2. Los nombres mienten

Sigue con la misma tabla. Mira los valores de Delito y de Delitos_code con count().

Verificación: cada una tiene 9 valores distintos, pero Delito contiene números del 1 al 9 y Delitos_code contiene nombres como Asesinato o Apropiacion Ilegal. Los nombres de las dos columnas están intercambiados.

Renómbralas con rename() para que digan la verdad, y de paso quítale el espacio al nombre de Area Policiaca.

En palabras: para usar Area Policiaca tal como viene hay que escribirla entre acentos graves. ¿Por qué? ¿Qué le habría hecho read.csv() a ese nombre?

# Escribe tu código aquí

18.16.3 Ejercicio 3. Un código no es un número

La columna con los números del 1 al 9 es un código, no una cantidad: no tiene sentido calcular su promedio. Vuelve a leer el archivo obligando a que esa columna se lea como texto, con col_types = cols(...) y col_character().

Verificación: class() de esa columna debe pasar de "numeric" a "character", y el resto de la tabla debe quedar igual (8870 por 7).

En palabras: da otro ejemplo de una columna que parece numérica y no lo es. ¿Qué se pierde si un código postal de Puerto Rico se lee como número?

# Escribe tu código aquí

18.16.4 Ejercicio 4. Ida y vuelta por el disco

Toma millas del paquete datos. Convierte clase en factor con mutate(clase = factor(clase)), guarda la tabla con write_csv() en Datos/millas_prueba.csv, y vuelve a leerla con read_csv().

Verificación: la tabla que vuelve tiene las mismas 234 filas, pero clase ya no es factor: es texto.

En palabras: explica por qué se perdió el tipo. ¿Qué formato usarías si necesitaras conservarlo? ¿Y por qué, aun así, seguimos usando .csv para compartir datos?

# Escribe tu código aquí

18.16.5 Ejercicio 5. Un archivo sucio, hecho a mano

Crea con writeLines() un archivo de texto que empiece con dos líneas de basura antes de la tabla y que use n/d para los faltantes:

Reporte mensual

Generado el 1 de agosto

parcela,plantas

P1,34

P2,n/d

P3,27

Léelo primero con read_csv() sin argumentos y mira el desastre. Después arréglalo con skip y na.

Verificación: la versión arreglada tiene 3 filas, 2 columnas, plantas es numérica y hay exactamente un NA.

En palabras: en la primera lectura, ¿cuántas columnas creó readr y de dónde sacó el nombre? Copia la advertencia que salió y explica qué te estaba diciendo.

# Escribe tu código aquí

18.16.6 Ejercicio 6. Números escritos en español

Convierte a número estos textos, que vienen escritos a la manera española: c("1.234", "2.500.000", "3,75"). Hazlo primero sin locale() y después con el locale() correcto.

Verificación: con el locale() correcto, "2.500.000" tiene que dar dos millones y medio, y "3,75" tiene que dar tres con setenta y cinco.

En palabras: sin locale(), ninguno de los tres da error, pero todos dan un número equivocado. Explica por qué este es el tipo de fallo más peligroso de todo el capítulo.

# Escribe tu código aquí

18.16.7 Ejercicio 7 (BONO). Fechas y horas de verdad

Vuelve a crimen_pr.csv. Comprueba con class() cómo llegaron Fecha y Hora. Después cuenta cuántos incidentes hay por mes.

Verificación: hay tres meses, todos de 2016, y el tercero tiene muchísimos menos casos que los otros dos.

En palabras: ¿por qué el tercer mes tiene tan pocos? Míralo con range(Fecha) y explica qué te dice eso sobre el archivo. ¿Qué error de análisis cometería alguien que comparara los tres meses sin darse cuenta?

# Escribe tu código aquí