Sesión 3. Limpieza y manipulación de datos

Algunos hacks que me gustan mucho:

Crear nuevo chunk = Ctrl + Alt + i En Mac = Cmd (o Options) + Alt + i

Flecha rápida <- Alt + - (guión)

Estructura de la sesión

  1. Cargar librerías y base de datos
  2. Análisis de una variable a la vez
  3. Función table
  4. Manipulación de bases de datos data.frame, función “select”, función “filter”
  5. Transformación de variables en bases de datos función “mutate”, as.factor, as.numeric, función “ifelse”
  6. Significado del operador %>% (pipe) y limpieza general de datos
  7. Árboles de decisión
  8. Random Forest

1. Cargamos las librerías a utilizar y la base de datos

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tree)
library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:dplyr':
## 
##     combine
eav21 <- read.csv("EAV_2021.csv")

¿Cómo saber si se cargó bien la base de datos? Imprime las primeras 6 filas:

# head(eav21)

2. ¿Cómo analizar solo una variable a la vez?

2 formas: ### Signo de pesos

head(eav21$m18_sex)
## [1] "Hombre" "Hombre" "Hombre" "Hombre" "Hombre" "Hombre"
head(eav21$p83)
## [1] 3 2 2 2 2 1

Función attach

attach(eav21)
head(m18_sex)
## [1] "Hombre" "Hombre" "Hombre" "Hombre" "Hombre" "Hombre"
head(p83)
## [1] 3 2 2 2 2 1

La función attach te permite escribir solo 1 vez el nombre de la base de datos, para motivos de practicidad y rapidez. Así puedes llamar a varias variables al mismo tiempo e incluirlas más rápidamente en una función o acción.

3. Función table

attach(eav21)
table(m05_nom)
## m05_nom
##                  Allende                  Anahuac                  Apodaca 
##                       12                       16                      351 
##                Aramberri               Bustamante        Cadereyta Jimenez 
##                        4                        4                      353 
##        Cienega de Flores            Doctor Arroyo                El Carmen 
##                       52                        8                       52 
##                  Galeana                   Garcia            General Bravo 
##                       12                      354                        8 
##         General Escobedo            General Teran           General Zuazua 
##                      354                        4                       52 
##                Guadalupe                  Hidalgo              Hualahuises 
##                      354                        8                        4 
##                   Juarez                  Linares              Los Ramones 
##                      354                       64                        4 
##                    Marin           Melchor Ocampo           Mier y Noriega 
##                        4                        4                        4 
##             Montemorelos                Monterrey                Pesqueria 
##                       44                      354                       80 
##                  Rayones          Sabinas Hidalgo         Salinas Victoria 
##                        4                       28                       50 
## San Nicolas de los Garza   San Pedro Garza Garcia           Santa Catarina 
##                      354                      346                      354 
##                 Santiago               Villaldama 
##                      348                        8

La función table te permite observar la frecuencia de cada término en una base de datos. En este caso, estamos analizando qué tantas encuestas se hicieron en cada municipio. Cuenta las coincidencias de caracteres y los convierte en una clase a cada una de estas.

Se pueden hacer incluso tablas cruzadas, es decir, cruzar una variable con otra y ver qué frecuencia tiene este cruce.

table(m05_nom, p83)
##                           p83
## m05_nom                      1   2   3   4   8   9
##   Allende                    2   9   1   0   0   0
##   Anahuac                    0  14   1   1   0   0
##   Apodaca                   31 263  51   5   1   0
##   Aramberri                  1   1   2   0   0   0
##   Bustamante                 0   4   0   0   0   0
##   Cadereyta Jimenez          9 160 161  21   1   1
##   Cienega de Flores          0  19  31   2   0   0
##   Doctor Arroyo              0   7   1   0   0   0
##   El Carmen                  0  36  16   0   0   0
##   Galeana                    0  12   0   0   0   0
##   Garcia                    14 204 132   3   1   0
##   General Bravo              1   5   1   1   0   0
##   General Escobedo          29 200 116   8   1   0
##   General Teran              3   1   0   0   0   0
##   General Zuazua             6  32   5   9   0   0
##   Guadalupe                 15 221 108   9   0   1
##   Hidalgo                    0   5   2   1   0   0
##   Hualahuises                0   4   0   0   0   0
##   Juarez                    13 198 135   8   0   0
##   Linares                    4  50   6   4   0   0
##   Los Ramones                0   3   1   0   0   0
##   Marin                      0   4   0   0   0   0
##   Melchor Ocampo             0   3   1   0   0   0
##   Mier y Noriega             0   3   1   0   0   0
##   Montemorelos               2  30  11   1   0   0
##   Monterrey                 37 227  84   6   0   0
##   Pesqueria                  1  43  34   2   0   0
##   Rayones                    2   2   0   0   0   0
##   Sabinas Hidalgo            0  23   5   0   0   0
##   Salinas Victoria           0  35  15   0   0   0
##   San Nicolas de los Garza  45 243  61   4   1   0
##   San Pedro Garza Garcia   121 211  13   1   0   0
##   Santa Catarina            52 228  62  11   0   1
##   Santiago                  46 256  42   3   1   0
##   Villaldama                 1   7   0   0   0   0

4. Manipulación de bases de datos

Función data.frame

attach(eav21)
dataframe_eav <- data.frame(m05_nom, m18_sex, p83, p96, p128)

Crea un dataframe, es decir, una tabla de datos con las variables que ingreses. Aún no la hemos asignado a una variable; esto es simplemente por motivos de exploración de los datos.

Función select

La siguiente función puede hacer lo mismo que la anterior. Sin embargo, select puede encadenarse con otras funciones en una “gran función”. Esto se verá en la sección “operador % (pipe)”

select_eav <- select(eav21, m05_nom, m18_sex, p83, p96, p128)

Función filter

Te permite seleccionar del data frame solo aquellas observaciones que cumplan con la condición de ser del municipio de San Pedro Garza García.

observaciones_spgg <- filter(eav21,
       m05_nom == 'San Pedro Garza Garcia')

Pequeño ejercicio: ¿Cómo podrías filtrar solamente las observaciones que corresponden al municipio de Juarez? Pista: es parecido al filtro realizado en el chunk anterior

5. Transformación de variables en bases de datos

Función mutate

Las variables en nuestra base de datos que corresponden a, por ejemplo, los municipios, son meros caracteres. R no sabrá cómo interpretarlos, pues para éste son simples letras sin un sentido o patrón aparente, aunque nosotrxs sepamos que corresponden a nombres de municipios.

¿Cómo podemos facilitar la labor de interpretación a R? #### Función as.factor

municipios <- mutate(eav21, m05_nom = as.factor(m05_nom))

Ahora R podrá fácilmente identificar todos estos caracteres como una clase o clasificación. Por lo tanto, Juarez será un factor diferente a Santa Catarina, y ahora podremos tener un análisis más profundo y rico de nuestros datos.

¿Puedes pensar en otras variables que puedan ser consideradas por R como factor para facilitar su análisis?

Función as.numeric

De igual manera, existen variables en nuestra base de datos que pueden ser consideradas no numéricas, pero que en realidad consisten en valores numéricos. En su caso, se tendría que usar “as.numeric()”, sin embargo, son raros estos casos, pues normalmente R lee las variables numéricas como “integers” por default.

Sin embargo, puede haber casos en los que algunos caracteres se hayan mezclado con valores numéricos en tu columna de observaciones, y por lo tanto as.numeric() jugaría un rol importante.

En este ejercicio, no lo usaremos puesto que R leyó todas las variables numéricas como lo que son, numéricas.

Función ifelse

seguridad_transporte <- select(eav21, p29_1_3)

## Filtrar todos los no sé, no contestó y no aplica porque no son relevantes para este ejercicio
seguridad_transporte <- filter(seguridad_transporte, !p29_1_3 %in% c(8, 9, 99))

seguridad_transporte <- mutate(seguridad_transporte, seguro.en.transporte = ifelse(p29_1_3 == '0','Inseguro','Seguro'))

seguridad_transporte <- mutate(seguridad_transporte, seguro.en.transporte = as.factor(seguro.en.transporte))

Todo lo anterior es muy cansado :( Nos llevó alrededor de 4 líneas llenas de texto para hacer lo que queríamos hacer.

6. ¿Se podría realizar todo lo anterior de manera más eficiente? ¡¡Síííí!!

Se puede utilizar un operador muy útil: %>% (pipe)

seguridad_transporte <- select(eav21, p29_1_3) %>% 
  filter(p29_1_3 %in% c(0, 1)) %>% # Aquí estamos filtrando 8, 9 y 99 a la vez sin necesidad de emplear varias líneas de código
  mutate(seguro.en.transporte = as.factor(ifelse(p29_1_3 == '0','Inseguro','Seguro')))

Lo que estamos haciendo en el anterior chunk es encadenar

Hack -> Puedes escribir el operador %>% más rápidamente con el siguiente comando: Para Windows: Ctrl + Shift + m Para Mac: Cmd (u Options) + Shift + m

Seleccionamos las variables de interés y omitimos las observaciones NA

# Municipio - m05_nom

# Sexo - m18_sex

# p83. ¿Qué tan seguro se siente en su municipio? 1. Muy seguro 2. Seguro 3. Inseguro 4. Muy inseguro   8. NS 9. NC

# p96. De acuerdo con su percepción, ¿Mencione cuál es el principal problema de SEGURIDAD que se vive en su municipio? 1    Delitos de alto impacto (homicidio, secuestro, violación). 2    Robos patrimoniales (robo a casa, vehículo, negocio o persona). 3   Delincuencia Organizada (venta de drogas, extorsiones, cobro de piso, presencia de grupos armados). 4   Violencia familiar y/o violencia contra la mujer. 5 Violencia en la colonia (riñas, conflicto entre vecinos, pandillerismo). 6  Todas. 7    Ninguno. 8  NS. 9   NC

# p128 1    Sin ingreso. 2  Menos de 1 SM ($1 - $4,250). 3  1-2 SM (4,251 - 8,501). 4   2-3 SM (8,502 - 12,752). 5  3-4 SM (12,753 - 17,003). 6 4-5 SM (17,004 - 21,254). 7 5-6 SM (21,255 - 25,505). 8 6-7 SM (25,506 - 29,756). 9 7-8 SM (29,757 - 34,007). 10    8-9 SM (34,008 - 38,258). 11    9-10 SM (38,259 - 42,509). 12   10 o más SM (42,510 o más). 13  No contesto

variables.seleccionadas <- eav21 %>% 
  select(m05_nom, m18_sex, p83, p96, p128)

variables.seleccionadas<-na.omit(variables.seleccionadas)

Revisamos el tipo de variables que tenemos y cambiemos a cómo las necesitamos creando nuevas variables

#m05_nom, es character, hay que cambiarlo a factor
#m18_sex, es character, hay que cambiarlo a factor
#p83, es numerica, para nuestro arbol vamos a dejarla como numerica
#p96, es númerica, hay que convertirla al texto que corresponde y luego a factor
#p128, es númerica pero hay que agregarle el salario máximo que corresponde a cada categoria

base.corregida<- variables.seleccionadas %>%
  filter(m05_nom!='Allende')%>%
  filter(m05_nom!='Anáhuac')%>%
  filter(m05_nom!='Aramberri')%>%
  filter(m05_nom!= 'Bustamante') %>% 
  mutate(municipio=as.factor(m05_nom))%>%
  mutate(sexo=as.factor(m18_sex))%>%
  mutate(nivel.de.seguridad=as.numeric(p83))%>%
  filter(nivel.de.seguridad%in%c(1,2,3,4))%>%
  mutate(problemas.de.inseguridad=ifelse(p96=='1', 'delitos.alto.impacto',
                                  ifelse(p96=='2', 'robo',
                                  ifelse(p96=='3', 'delincuencia.organizada',
                                  ifelse(p96=='4', 'violencia.familiar',
                                  ifelse(p96=='5', 'violencia.en.la.colonia',
                                  ifelse(p96=='6', 'todos',
                                  ifelse(p96=='7', 'ninguno','NA'))))))))%>%
  filter(problemas.de.inseguridad!='NA')%>%
  mutate(maximo.ingreso=ifelse(p128=='1', 0,
                        ifelse(p128=='2', 4250,
                        ifelse(p128=='3', 8501,
                        ifelse(p128=='4', 12752,
                        ifelse(p128=='5', 17003,
                        ifelse(p128=='6', 21254,
                        ifelse(p128=='7', 25505,
                        ifelse(p128=='8', 29756,
                        ifelse(p128=='9', 34007,
                        ifelse(p128=='10', 38258,
                        ifelse(p128=='11', 42509,
                        ifelse(p128=='12', 50000,'NA')))))))))))))%>%
  filter(maximo.ingreso != 'NA')%>%
  select(nivel.de.seguridad,municipio,sexo,problemas.de.inseguridad,maximo.ingreso) %>% 
  mutate(problemas.de.inseguridad = as.factor(problemas.de.inseguridad)) %>% 
  mutate(maximo.ingreso = as.numeric(maximo.ingreso))

7. Árboles de decisión

Los árboles de decisión son una visualización muy útil para aquellas personas que no tienen conocimientos en R, puesto que es muy fácil de interpretar y leer y nos puede ayudar a comunicar un mensaje de manera más eficiente.

En todo árbol de decisión, queremos

Para nuestro arbol de decisión utilizaremos nivel de seguridad como respuesta a predecir. La estructura en la función tree es la siguiente nombre del arbol de decisión <- tree(nombre de la variable que se quiere predecir ~ las variables predictoras, data = nombre de la base de datos) summary(nombre del arbol de decisión)

eav.tree<-tree(nivel.de.seguridad~., data=base.corregida)
summary(eav.tree)
## 
## Regression tree:
## tree(formula = nivel.de.seguridad ~ ., data = base.corregida)
## Variables actually used in tree construction:
## [1] "municipio"                "problemas.de.inseguridad"
## [3] "maximo.ingreso"          
## Number of terminal nodes:  5 
## Residual mean deviance:  0.3511 = 1194 / 3401 
## Distribution of residuals:
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -1.43100 -0.43060 -0.09576  0.00000  0.56940  2.43800

Ploteamos nuestro arbol

plot(eav.tree)
text(eav.tree, pretty=0, cex=0.7)

Para contar con una predicción con mayor precisión, es necesario imponerle ciertos criterios a R que le permitan tener menos sesgo en sus predicciones.

Si le decimos a R que divida el conjunto de datos en dos grupos, y que uno sea para entrenar a nuestro algoritmo, y otro que pruebe qué tan certero fue nuestro algoritmo en las predicciones, es posible contar con un mejor modelo de predicción.

Árbol con datos de entrenamiento

set.seed(555)
train <- sample(1:nrow(base.corregida), nrow(base.corregida)/2)
eav.data.test<-base.corregida[-train, ]
eav.tree<-tree(nivel.de.seguridad~., data=base.corregida, subset=train)
summary(eav.tree)
## 
## Regression tree:
## tree(formula = nivel.de.seguridad ~ ., data = base.corregida, 
##     subset = train)
## Variables actually used in tree construction:
## [1] "municipio"                "problemas.de.inseguridad"
## [3] "maximo.ingreso"          
## Number of terminal nodes:  5 
## Residual mean deviance:  0.3454 = 586.5 / 1698 
## Distribution of residuals:
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -1.45300 -0.45250 -0.05747  0.00000  0.54750  2.40100

Aquí estamos realizando un árbol que se está entrenando solamente con los datos en nuestro conjunto de entrenamiento, para así aprender a predecir qué tan segura o insegura una persona se sentirá en su municipio.

Lo revisamos contra el conjunto de prueba

eav.pred<-predict(eav.tree, data=eav.data.test)
eav.test<-base.corregida[-train,"nivel.de.seguridad"]
sqrt(mean((eav.pred-eav.test)^2))
## [1] 0.6717388

Recordemos que este valor nos indica que nuestra predicción se encuentra en un +-

8. Random Forest

Una herramienta útil en buscar qué variables son las más relevantes en nuestro modelo de regresión es la llamada RandomForest.

Apliquemos ahora Random Forest

set.seed(555)
rf.eav<-randomForest(nivel.de.seguridad~.,data=base.corregida,subset=train,mtry=2,importance=TRUE)
## Warning in randomForest.default(m, y, ...): The response has five or fewer
## unique values. Are you sure you want to do regression?
rf.eav
## 
## Call:
##  randomForest(formula = nivel.de.seguridad ~ ., data = base.corregida,      mtry = 2, importance = TRUE, subset = train) 
##                Type of random forest: regression
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##           Mean of squared residuals: 0.3566239
##                     % Var explained: 12.85

mtry es igual a 2 puesto que en una regresión lineal, el criterio del valor de este argumento debe ser el siguiente: mtry = número de variables/3 mtry = 5 / 3 = 1.66666 mtry se redondea a 2

Verificamos su importancia

importance(rf.eav)
##                            %IncMSE IncNodePurity
## municipio                63.092693     158.35026
## sexo                     -2.311094      18.86931
## problemas.de.inseguridad 44.415015      83.59263
## maximo.ingreso           20.974143      75.76719
varImpPlot(rf.eav)

9. Actividad 3

Las actividades a realizar son las siguientes: - Crear una nueva base de datos con el nombre “base_tarea” que contenga las observaciones solamente de los municipios (m05_nom) de Apodaca, San Pedro Garza Garcia, San Nicolas de los Garza, General Escobedo y Monterrey. Asimismo, asegúrate que contenga las variables de genero y nivel de inseguridad que usamos en el ejercicio de RandomForest (m18_sex, p83). NO ES NECESARIO QUE LOS CONVIERTAS A FACTOR.

Pista: recuerda que para filtrar varios valores a la vez, puedes usar %in% seguido de la concatenación de los valores que quieres dentro de tu filtro.