2.4 Gestión de datos en R

En R, un marco de datos corresponde a lo que otros paquetes estadísticos llaman una matriz de datos o un conjunto de datos. Por lo general, es una matriz que consta de una lista de vectores y/o factores de idéntica longitud, lo que produce un formato rectangular donde las columnas corresponden a variables y las filas a observaciones.

Creación desde cero

Se puede crear un conjunto de datos simple artificialmente, con tres variables llamadas “uno,” “dos” y “tres,” mediante el uso de:

mis_datos <- data.frame(uno = 1:10, dos = 11:20, tres = 21:30)

Alternativamente, mis_datos se puede crear usando:

mis_datos <- as.data.frame(matrix(1:30, ncol = 3))
names(mis_datos) <- c("uno", "dos", "tres")

El código primero crea una matriz de tamaño \(10 \times 3\) que posteriormente se coacciona en un marco de datos y cuyos nombres de variable se cambian finalmente a “uno,” “dos” y “tres.”

Se debe tener en cuenta que se puede utilizar la misma sintaxis tanto para consultar como para modificar los nombres en un marco de datos. Además, vale la pena reiterar que aunque un marco de datos se puede coaccionar a partir de una matriz como se indicó anteriormente, se representa internamente como una lista.

Selección de subconjuntos

Es posible acceder a un subconjunto de variables (es decir, columnas) a través de [ o $, donde este último solo puede extraer una sola variable. Por tanto, la segunda variable (dos) se puede seleccionar a través de:

mis_datos$dos
#>  [1] 11 12 13 14 15 16 17 18 19 20
mis_datos[, "dos"]
#>  [1] 11 12 13 14 15 16 17 18 19 20
mis_datos[, 2]
#>  [1] 11 12 13 14 15 16 17 18 19 20

En todos los casos, el objeto devuelto es un vector simple; es decir, los atributos del marco de datos se eliminan (por defecto).

Para simplificar el acceso a las variables en un determinado conjunto de datos, se pueden adjuntar los nombres mediante la función attach(). Técnicamente, esto significa que el conjunto de datos adjunto se agrega a la ruta de search() y, por ende, las variables contenidas en este conjunto de datos se pueden encontrar cuando su nombre se usa en un comando. Se puede poner en práctica lo anterior mediante:

# mean(dos) por sí solo muestra un error. En consecuencia,
attach(mis_datos)
mean(dos)
#> [1] 15.5
detach(mis_datos)

Los marcos de datos deben adjuntarse con cuidado; en particular, se debe prestar atención a no adjuntar varios marcos de datos con los mismos nombres de columna o tener una variable con el mismo nombre en el entorno global, ya que es probable que esto genere confusión. Para evitar adjuntar y desadjuntar un conjunto de datos para un solo comando, la función with() puede ser útil, como en:

with(mis_datos, mean(dos))
#> [1] 15.5

A menudo es necesario trabajar con subconjuntos de un marco de datos; es decir, utilizar solo observaciones seleccionadas (= filas) y/o variables (= columnas). Esto se puede hacer nuevamente a través de [ o, más convenientemente, usando el comando subset(), cuyos principales argumentos son un marco de datos del que se tomará el subconjunto y una declaración lógica que define los elementos a seleccionar. Por ejemplo:

mis_datos.sub <- subset(mis_datos, dos <= 16, select = -dos)

El código anterior toma todas las observaciones cuyo valor de la segunda variable (dos) no excedan de \(16\) (se sabe que existen seis observaciones con esta propiedad) y, además, todas las variables a excepción de dos están seleccionadas.

Importar y exportar

Para exportar marcos de datos en formato de texto sin formato, la función write.table() se puede emplear:

write.table(mis_datos, file = "mis_datos.txt", col.names = TRUE)

Crea un archivo de texto mis_datos.txt en el directorio de trabajo actual. Si este conjunto de datos se va a utilizar de nuevo, en otra sesión, se puede importar utilizando:

nuevos_datos <- read.table("mis_datos.txt", header = TRUE)

La función read.table() devuelve un objeto de la clase “marco de datos” (data.frame), que luego se asigna al nuevo objeto nuevos_datos. Configurando col.names = TRUE, los nombres de las columnas están escritos en la primera línea de mis_datos.txt y, por lo cual, se establece header = TRUE al leer el archivo de nuevo. La función write.table() es bastante flexible y permite especificar el símbolo de separación y el separador decimal, entre otras propiedades del archivo a escribir, de modo que se pueden producir varios formatos basados en texto, incluidos valores separados por tabuladores o comas.

Dado que este último es un formato popular para intercambiar datos (ya que puede ser leído y escrito por muchos programas de hojas de cálculo, incluido Microsoft Excel), las interfaces de conveniencia read.csv() y write.csv() están disponibles. De manera similar, read.csv2() y write.csv2() proporcionar exportación e importación de valores separados por punto y coma, un formato que se usa típicamente en sistemas que emplean la coma (y no el punto) como separador decimal. Además, existe una función más elemental, denominada scan(), para datos que no se ajustan al diseño matricial requerido por read.table().^[Un ejemplo son las respectivas páginas del manual de R y el manual “R Data Import/Export” (R Development Core Team 2021c para más detalles).

También es posible guardar los datos en el formato binario interno de R, por convención con extensión .RData o .rda. El siguiente comando guarda los datos en formato binario en R.

save(mis_datos, file = "mis_datos.rda")

Los archivos binarios se pueden cargar usando:

load("mis_datos.rda")

En contraste con read.table(), esto no devuelve un solo objeto. En su lugar, hace que todos los objetos almacenados en mis_datos.rda estén directamente disponible en el entorno actual. La ventaja de usar archivos .rda es que varios objetos de R se pueden almacenar (en realidad varios objetos arbitrarios de R se pueden almacenar, incluidas funciones o modelos ajustados), sin pérdida de información.

Todos los conjuntos de datos del paquete AER se suministran en este formato binario (vaya a la carpeta ~/AER/dataen la biblioteca de R para comprobarlo). Dado que son parte de un paquete, su acceso se hace mucho más fácil usando data() (que en este caso establece la correspondiente llamada load()). Por tanto, el siguiente código carga el marco de datos Journals del paquete AER (almacenado en el archivo ~/AER/data/Journals.rda).^[El conjunto de datos utilizado en el Ejemplo 1 de los Ejemplos introductorios a sesiones típicas en R.

data("Journals", package = "AER")

Si el argumento package se omite, todos los paquetes que se encuentran actualmente en la ruta de búsqueda son verificados para proporcionar el conjunto de datos Journals.

Leer y escribir formatos binarios extranjeros

R también puede leer y escribir una serie de formatos binarios de otros propietarios, en particular archivos S-PLUS, SPSS, SAS, Stata, Minitab, Systat y dBase, usando la función que proporciona el paquete foreign ( parte de un estándar de instalación de R). La mayoría de los comandos están diseñados para ser similares a read.table() y write.table(). Por ejemplo, para los archivos de Stata, ambos read.dta() y write.dta() están disponibles y se pueden utilizar para crear un archivo Stata que contenga mis_datos.

library("foreign")
write.dta(mis_datos, file = "mis_datos.dta")

Los archivos se pueden leer en R a través de:

mis_datos <- read.dta("mis_datos.dta")

Consulte la documentación del paquete foreign para mayor información.

Interacción con el sistema de archivos y manipulaciones de cadenas

En los párrafos anteriores fue necesaria cierta interacción con el sistema de archivos para leer y escribir archivos de datos. R posee una rica funcionalidad para interactuar con archivos externos y comunicarse con el sistema operativo. Esto esta más allá del alcance de este curso, pero me gustaría proporcionar al lector interesado algunos consejos que pueden servir como base para lecturas adicionales.

Los archivos disponibles en un directorio o carpeta se pueden consultar a través de la función dir() y también copiar (usando file.copy()) o eliminar (usando file.remove()) independiente del sistema operativo. Por ejemplo, el archivo Stata creado anteriormente se puede eliminar de nuevo desde R vía:

file.remove("mis_datos.dta")

Otros comandos (potencialmente dependientes del sistema) se pueden enviar como cadenas al sistema operativo usando system(). Consulte las respectivas páginas del manual para obtener más información y ejemplos resueltos.

Anteriormente, se discutió cómo los objetos de datos (especialmente los marcos de datos) se pueden escribir como archivos en varios formatos. Más allá de eso, a menudo se desean guardar comandos o salida en archivos de texto. Una posibilidad para lograr esto es usar sink(), que puede dirigir la salida a una conexión file() en la que se podrán escribir las cadenas con cat(). En algunas situaciones writeLines() es más conveniente para esto. Además, dump() puede crear representaciones de texto de objetos en R y escribirlos en una conexión file().

A veces, es necesario manipular las cadenas antes de crear una salida. R también proporciona una funcionalidad rica y flexible para esto. Las tareas típicas incluyen dividir cadenas (strsplit()) y/o pegarlas juntas (paste()). Para combinar y reemplazar patrones, grep() y gsub() están disponibles, que también admiten expresiones regulares. Para combinar texto y valores variables, sprintf() es útil.

Factores

Los factores son una extensión de los vectores diseñados para almacenar información categórica. Los ejemplos econométricos típicos de variables categóricas incluyen género, afiliación sindical o etnia. En muchos paquetes de software, estos se crean utilizando una codificación numérica (por ejemplo, \(0\) para los hombres y \(1\) para mujeres); a veces, especialmente en entornos de regresión, una sola variable categórica se almacena en varias de estas variables ficticias (o dummy) si existen más de dos categorías.

En R, las variables categóricas deben especificarse como factores. Por ejemplo, primero se crea un vector codificado ficticio con un cierto patrón y luego se transforma en un factor usando factor():

g <- rep(0:1, c(2, 4))
g <- factor(g, levels = 0:1, labels = c("masculino", "femenino"))
g
#> [1] masculino masculino femenino  femenino  femenino  femenino 
#> Levels: masculino femenino

La terminología es que un factor tiene un conjunto de niveles, digamos k niveles. Internamente, un factor de k-niveles consta de dos elementos:

  1. Un vector de enteros entre \(1\) y k.
  2. Un vector de caracteres, de longitud k, que contiene cadenas con las etiquetas correspondientes.

Arriba, se creó el factor a partir de un vector entero; alternativamente, podría haberse construido a partir de otros vectores numéricos, de caracteres o lógicos. La información ordinal también se puede almacenar en un factor estableciendo el argumento ordered = TRUE al llamar a la función factor().

La ventaja de este enfoque es que R sabe cuándo una determinada variable es categórica y puede elegir los métodos apropiados automáticamente. Por ejemplo, las etiquetas se pueden usar en la salida impresa, se pueden elegir diferentes métodos de resumen y trazado, así como se pueden calcular codificaciones de contraste (por ejemplo, variables ficticias) en regresiones lineales. Tenga en cuenta que para estas acciones el orden de los niveles puede ser importante.

Valores faltantes

Muchos conjuntos de datos contienen observaciones para las cuales ciertas variables no están disponibles. El software econométrico necesita formas de lidiar con esto. En R, tales valores perdidos se codifican como N/A ( para “no disponible” o not available). Todos los cálculos estándar en N/A se vuelven N/A.

Es necesario tener especial cuidado al leer datos que utilizan una codificación diferente. Por ejemplo, al preparar el paquete AER, se pueden encontrar varios conjuntos de datos que empleaban \(999\) para los valores perdidos. Si un archivo mis_datos.txt contiene valores perdidos codificados de esta manera, se pueden convertir a N/A usando el argumento na.strings al leer el archivo:

newdata <- read.table("mis_datos.txt", na.strings = "-999")

Para consultar si ciertas observaciones son N/A o no, se debe usar la función is.na().