El análisis de datos siempre debe comenzar con un proceso de limpieza de datos que forma parte del preprocesamiento fundamental para cualquier análisis posterior, teniendo certeza de contar con datos eficientes, precisos y efectivos.
Limpiar datos conlleva a analizar, identificar y corregir los datos “crudos” que están desordenados, equivocados o mal procesados. En este proceso se destacan las siguientes tareas: manejo de valores faltantes, manejo de valores atípicos, corregir errores, determinar si la información está de forma correcta y organizarla para los objetivos propuestos.
A continuación presentamos un resumen detallado de tareas dedicadas a la limpieza de datos. Es importante resaltar que estas tareas dependen del tipo de dato y de los objetivos del análisis. Por lo tanto, para cada trabajo se deberá analizar la utilidad de cada una de ellas.
En muchas ocasiones la información que necesitamos consolidar para una investigación, se debe reunir de más de una base de datos. Por lo tanto, tener claro como hacerlo sin cometer errores de perder información es muy importante.
Para esta tarea vamos a usar las bases de datos de morosidad (morosidad16 y morosidad17). Estas base de datos contiene información de un banco de Costa Rica.
Para importar los datos, recuerden cambiar el directorio de trabajo y
seleccionar la carpeta en donde guarden los archivos archivos. Esto se
hace con el comando setwd()
.
Podemos importar los archivos de dos formas:
Podemos hacerlo desde el menú de arriba, usando: Import Dataset
y seleccionamos el tipo de archivo que queremos importar. Veamos que
estas bases de datos están en formato .xlsx
Podemos hacerlo escribiendo el código. En esta ocasión vamos a
importar archivos .xlsx, por lo que usamos la librería
readxl
,
que previamente debemos instalar.
# Recuerden cambiar el directorio de trabajo
# setwd("C:/Users/Tecnico/OneDrive - University of Puerto Rico/Cursos/ESTA 5504/Bases de datos")
library(readxl)
library(printr) # visualización de tablas
morosidad16 <- read_xlsx("morosidad16.xlsx")
morosidad17 <- read_xlsx("morosidad17.xlsx")
head(morosidad16)
head(morosidad17)
Ambas bases de datos tienen las siguientes variables:
Para unir bases de datos necesitamos una o más variables en común entre las dos bases de datos. Existen varias funciones para unir bases de datos. Veamos a continuación dos de las más usadas:
Sirve para unir columnas de dos bases de datos diferentes. Para unir bases es clave hacerse la pregunta: ¿Queremos conservar todas las variables o solo las que sean comunes?. De acuerdo a la respuesta, va a depender la fórmula que se utilice.
La sintaxis de la función merge()
se puede entender en la siguiente imagen.
La sintaxis es la siguiente:
Unir solo casos comunes:
merge(base1, base2, by="nombre variable")
merge(base1, base2, by.x="nombre variable base 1", by.y="nombre variable base 2")
Unir todos los casos:
merge(base1, base2, by="nombre variable", all=TRUE)
Unir por más de una variable:
merge(base1, base2, by=c("variable1","variable2"))
merge(base1, base2, by.x=c("variable1", "variable2"), by.y=c("variable1", "variable2"))
Para más detalles visita: función merge()
Algunos ejemplos de las diferentes formas de unir dos bases de datos se presentan a continuación:
# Unir solo casos comunes:
morosidad1 <- merge(morosidad17, morosidad16, by= "id")
# head(morosidad1)
# Unir todos los casos:
morosidad2 <-merge(morosidad17, morosidad16, by= "id", all=TRUE)
# head(morosidad2)
# Unir por más de una variable:
morosidad3 <- merge(morosidad17, morosidad16, by=c("id","nombre","situacion","lugar.pago"))
# head(morosidad3)
También podemos unir bases con funciones de la librería dplyr
.
La sintaxis de las diferentes funciones se puede entender en la
siguiente imagen.
¿Qué pasa si las variables se llama diferente?
La sintaxis de by
sería: by = c("variable1" = "variable2")
.
¿Qué pasa si queremos unir por más de una variable?
La sintaxis de by
sería: by = c("variable1", "variable2")
.
Para más detalles visita: dplyr: joins
Apliquemos una de las funciones para las dos bases de ejemplo y queda como ejercicio practicar las demás funciones.
library(dplyr)
morosidad4 <- inner_join(morosidad17, morosidad16, by= c("id","nombre","situacion","lugar.pago"))
Es de vital importancia explorar correctamente los datos. Esta tarea se refiere al proceso de analizar y examinar un conjunto de datos con el objetivo de entender su estructura, identificar patrones, tendencias, y obtener una visión general de la información que contienen. Algunas funciones básicas para realizar esta tarea son:
View()
:
permite visualizar la base de datos en el formato de hoja de cálculo
interno de R.View(morosidad4)
dim()
:
permite consultar la dimensión de la base de datos.dim(morosidad4)
## [1] 50650 7
head()
:
permite ver la información de las primeras filas de la base de datos.
Incluso podemos seleccionar la cantidad de filas que queremos ver, por
ejemplo: head(base, n=20)
,
lo cual nos muestra las primeras 20 filas. También podemos ver que nos
muestra el tipo de variable.head(morosidad4)
tail()
:
permite ver las últimas filas. Tiene la misma sintaxis que head()
.tail(morosidad4)
glimpse()
:
permite explorar las características de las variables. Por ejemplo, nos
dice que la variable nombre es carácter y que las
variables deuda17 y deuda16 es
“double”, el cual es un formato numérico. Funciona similar a la función
str()
.glimpse(morosidad4)
La manipulación de los datos es una tarea casi que obligatoria para cualquier análisis, pues en la gran mayoría de veces los datos “crudos” deben reorganizarse para el análisis. Es el proceso de realizar diversas operaciones y acciones sobre un conjunto de datos. Estas operaciones pueden incluir la inserción, actualización, eliminación y consulta de datos con el objetivo de gestionar y organizar la información de manera eficiente.
Existen dos formatos habituales para estructurar datos: formato ancho o formato largo.
La mayoría de programas estadísticos suponen que los datos están en formato largo, lo que significa que las columnas representan variables y las filas observaciones, de manera que cada dato pertenece a una variable y una observación única. Las variables (columnas) contienen valores que miden la misma característica o atributo (edad, estatura, etc.) en cada unidad experimental. Una observación (fila) contiene los valores medidos en la misma unidad experimental (persona, objeto, cosa, etc.) en todos atributos estudiados. En resumen, se conoce como base de datos ordenada si:
Un ejemplo de una base de datos presentada en los dos formatos se puede apreciar en la siguiente imagen.
A menudo los conjuntos de datos no están ordenados y violan alguna de estas condiciones. Lo más común es encontrarnos con:
Para facilitar el preprocesamiento y posterior análisis de los datos
es recomendable construir la base de datos en el formato que mejor se
ajuste al trabajo a realizar. Para desarrollar esta tarea, se suele
trabajar con dos librerías: tidyr
y dplyr
.
Esta librería brinda muchas funcionalidades para realizar transformaciones en una base de datos con el fin de poder transformarlo a la estructura que deseemos. El primer paso para esto es averiguar cuales son las variables y cuales son las observaciones. Una vez que las hemos identificado, nos podemos enfrentar a diferentes tipos de problemas:
Una variable está dividida en múltiples columnas.
Una observación está dispersa en múltiples filas.
Múltiples variables están metidas en una única celda.
Para remediar estos posibles problemas, la librería tidyr
proporciona 4 funciones principales: gather()
,
spread()
,
separate()
,
unite()
.
Para explicar el funcionamiento de estas funciones, crearemos dos pequeñas bases de datos.
library(tidyr)
Estudiantes <- data.frame(Grado = c("A","B","C","D","E"),
Hombre = c(11,15,15,15,17),
Mujer = c(15,10,12,15,14))
Estudiantes2 <- data.frame(Grado = c("A","B","C","D","E"),
Hombre_1 = c(3,6,7,4,1),
Mujer_1 = c(3,6,7,4,1),
Hombre_2 = c(3,3,3,8,2),
Mujer_2 = c(4,5,8,1,7))
A continuación, veamos como trabajar con cada una de estas funciones.
La función toma las columnas múltiples, las colapsa en una sola y
crea una nueva columna con los valores respectivos. Es decir, convierte
la base de datos en formato largo. La sintaxis es gather(data, key, value, columnas)
,
donde:
key
: nombre de la columna que vamos a
colapsar.
value
: nombre de la variable que va a guardar los
valores.
columna
: las columnas que se van a colapsar. Podemos
ponerlas separadas por coma, o podemos usar el operador -
para seleccionar las columnas que no se utilizarán.
Por ejemplo, veamos la estructura de Estudiantes.
Estudiantes
Grado | Hombre | Mujer |
---|---|---|
A | 11 | 15 |
B | 15 | 10 |
C | 15 | 12 |
D | 15 | 15 |
E | 17 | 14 |
Para reestructurar Estudiantes, pensado en colocarle un formato largo y corregir el posible errores presentado, el comando sería:
Estudiantes_long <- gather(Estudiantes, Sexo, Frecuencia, -Grado)
Es la función contraria a gather()
,
que nos convierte la base a formato ancho. Es decir, nos devolvería a la
tabla original.
Para los datos del ejemplo, esto sería:
Estudiantes_wide <- spread(Estudiantes_long, Sexo, Frecuencia)
Esta función nos permite separar columnas. La sintaxis es: separate(data, col, into, sep)
,
donde:
col
: la columna que hay que separar.into
: las columnas por las que vamos a separar. Se
pueden poner como vector de la forma
c(“col1”, “col2”)
.sep
: separador, por ejemplo comas, puntos, guion bajo u
otros caracteres. Si no se especifica el argumento R
trata de identificar el patrón para separar los datos.Como ejemplo, analicemos la base de daros Estudiantes2.
Estudiantes2
Grado | Hombre_1 | Mujer_1 | Hombre_2 | Mujer_2 |
---|---|---|---|---|
A | 3 | 3 | 3 | 4 |
B | 6 | 6 | 3 | 5 |
C | 7 | 7 | 3 | 8 |
D | 4 | 4 | 8 | 1 |
E | 1 | 1 | 2 | 7 |
En este caso tenemos un doble problema: (1) tenemos valores de una misma variable en diferentes columnas y (2) diferentes variables en una sola. En este caso, nos gustaría separar a los hombres y mujeres según la clase en la que están: 1 y 2.
Entonces tenemos que hacer dos pasos. Primero usamos la función gather()
:
Estudiantes2_long1 <- gather(Estudiantes2, sexo_clase, Frecuencia, -Grado)
Como segundo paso, usamos la función separate()
.
Aquí, queremos separar el Sexo de la
Clase. Esto es:
Estudiantes2_long2 <- separate(Estudiantes2_long1, sexo_clase, c("Sexo", "Clase"))
#En este caso R detectó el carácter para separar, el resultado es igual a si hubiéramos puesto la opción sep="_"
Es la función contraria a separate()
.
Como ejemplo, si queremos volver a la tabla original:
estudiantes2_unida <- unite(Estudiantes2_long2, sexo_clase, Sexo, Clase, sep="-")
Para más detalles de estas funciones, puede visitar: tidyr
Esta librería proporciona una forma bastante ágil de manejar los bases de datos. Además, el código escrito usando este paquete (especialmente usando la sintaxis en cadena que veremos más adelante) es mucho más legible y fácil de entender que el habitual.
El paquete incluye un conjunto de comandos que coinciden con las
acciones más comunes que se realizan sobre un conjunto de datos
(seleccionar columnas select()
,
seleccionar filas filter()
,
ordenar arrange()
,
añadir nuevas variables mutate()
,
resumir mediante alguna medida numérica summarise()
.
Además, tiene dos sintaxis interesantes que ayudan a resumir lineas de
código.
A continuación, veamos como trabajar con cada una de estas funciones.
Nos permite seleccionar columnas. La sintaxis sería: select(dataframe, col1, col2)
,
donde col1
, col2
, se refiere a los nombres de
las columnas que queramos seleccionar.
Algunos ejemplos:
# seleccionar únicamente las columnas de id y deuda:
select(morosidad4, id, deuda17, deuda16)
# seleccionar todas las columnas menos Estado:
select(morosidad4, -Estado)
# seleccionar un rango de columnas:
select(morosidad4, id:situacion)
Nos permite filtrar filas. La sintaxis es: filter(dataframe, condicion)
,
donde condicion
es una condición lógica por la que queremos
filtrar datos.
Algunos ejemplos:
# filtrar deudas 2017 mayores a 1000000:
filter(morosidad4, deuda17>1000000)
# filtrar deudas que crecieron entre 2016 y 2017:
filter(morosidad4, deuda17>deuda16)
# filtrar solo deudas mayores a un millón y de difícil cobro:
filter(morosidad4, deuda17>deuda16)
# filtrar solo casos que han estado morosos por los dos años:
filter(morosidad4, !is.na(deuda17), !is.na(deuda16))
Nos permite crear nuevas columnas de forma fácil. La sintaxis es:
mutate(dataframe, nueva_variable=condicion)
.
Algunos ejemplos:
# Crear una variable sobre cambio en la deuda:
morosidad5 <- mutate(morosidad4, cambio.deuda=deuda17-deuda16)
# Crear una variable que categorice el cambio en la deuda en si aumentó o no:
morosidad5 <- mutate(morosidad5, tipo.cambio=ifelse(cambio.deuda<0,"disminuyo", "aumento"))
# Crear múltiples variables a la vez:
morosidad6 <- mutate(morosidad4, cambio.deuda=deuda17-deuda16,
tipo.cambio=ifelse(cambio.deuda<0,"disminuyo", "aumento"))
Nos permite ordenar las base por una o varias columnas. La sintaxis
es: arrange(dataframe,val1,val2)
,
donde val1
, val2
, se refiere a los nombres de
las variables con las que se desea ordenar.
Algunos ejemplos:
# ordenar en orden ascendente por deuda17 y por cambio.deuda:
morosidad5 <- arrange(morosidad5, deuda17, cambio.deuda)
# Si lo queremos en orden descendente usamos desc():
morosidad5 <- arrange(morosidad5, desc(deuda17), desc(cambio.deuda))
Nos permite resumir mediante alguna medida numérica.
Un ejemplo:
# Calculamos la suma de todas las deudas
summarise(morosidad4, media = mean(deuda17, na.rm=T), sd = sd(deuda17, na.rm=T))
Además de trabajar con las anteriores funciones, existen dos sintaxis que nos ayudan a simplificar el trabajo.
Este operador nos va a facilitar muchísimo el trabajo con funciones y
nos permite hacer comando con menos líneas de código. Funciona colocando
dataframe %>% funcion()
.
Esto nos ahorra estar poniendo como primer argumento de las funciones de
dplyr al objeto.
Por ejemplo, podemos usar las funciones anteriores de la siguiente manera:
morosidad7 <- morosidad4 %>%
select(id,nombre,deuda17,deuda16,Estado) %>%
filter(!is.na(deuda17), !is.na(deuda16)) %>%
mutate(cambio.deuda=deuda17-deuda16,
tipo.cambio=ifelse(cambio.deuda<0,"disminuyó", "aumentó")) %>%
arrange(desc(deuda17), desc(cambio.deuda))
Nos permite configurar la variable de agrupación y aplicar otras funciones.
Por ejemplo:
morosidad8 <- morosidad5 %>% group_by(situacion) %>%
summarise(media = mean(deuda17, na.rm=T), sd = sd(deuda17, na.rm=T))
Para más detalles de estas funciones, puede visitar: dplyr