Capítulo 2 Introducción a R

R (R Core Team) es un entorno y lenguaje de programación que permite el análisis estadístico de información y reportes gráficos. Es ampliamente usado en investigación por la comunidad estadística en campos como la biomedicina, minería de datos, matemáticas financieras, entre otros. Ha ganado mucha popularidad en los últimos años al ser un software libre que está en constante crecimiento por las aportaciones de otros usuarios y que permite la interacción con software estadísticos como STATA, SAS, SPSS, etc.. R permite la incorporación de librerías y paqueterías con funcionalidades específicas, por lo que es un lenguaje de programación muy completo y fácil de usar.

2.1 ¿Cómo obtener R?

R puede ser fácilmente descargado de forma gratuita desde el sitio oficial http://www.r-project.org/. R está disponible para las plataformas Windows, Mac y Linux.

2.2 ¿Qué es RStudio?

RStudio es un Entorno de Desarrollo Integrado (IDE, por sus siglas en inglés) para R. Este permite y facilita el desarrollo y ejecución de sintaxis para código en R, incluye una consola y proporciona herramientas para la gestión del espacio de trabajo. RStudio está disponible para Windows, Mac y Linux o para navegadores conectados a RStudio Server o RStudio Server Pro.

Algunas de las principales características de Rstudio que lo hacen una gran herramienta para trabajar en R, son:

  • Auto completado de código
  • Sangría inteligente
  • Resaltado de sintaxis
  • Facilidad para definir funciones
  • Soporte integrado
  • Documentación integrada
  • Administración de directorios y proyectos
  • Visor de datos
  • Depurador interactivo para corregir errores
  • Conección con Rmarkwon y Sweave

La siguiente imagen muestra la forma en la que está estructurado RStudio. El orden de los páneles puede ser elegido por el usuario, así como las características de tipo de letra, tamaño y color de fondo, entre otras características.

Páneles de trabajo de Rstudio

Figure 2.1: Páneles de trabajo de Rstudio

2.3 Lectura de datos

El primer paso para analizar datos es incorporarlos a la sesión de R para que puedan ser manipulados y observados. Existen múltiples librerías y funciones en R que permiten leer la información proveniente de un archivo externo, el cual puede tener una de muchas posibles extensiones.

Usualmente, no creamos los datos desde la sesión de R, sino que a través de un archivo externo se realiza la lectura de datos escritos en un archivo. Los más comúnes son:

La paquetería readr fue desarrollada recientemente para lidiar con la lectura de archivos grandes rápidamente. Esta paquetería proporciona funciones que suelen ser mucho más rápidas que las funciones base que proporciona R.

Ventajas de readr:

  • Por lo general, son mucho más rápidos (~ 10x) que sus funciones equivalentes.

  • Producen tibbles:

    • No convierten vectores de caracteres en factores.
    • No usan nombres de filas ni modifican los nombres de columnas.
  • Reproducibilidad

2.3.1 Archivos csv

A la hora de importar conjuntos de datos en R, uno de los formatos más habituales en los que hallamos información es en archivos separados por comas (comma separated values), cuya extensión suele ser .csv. En ellos encontramos múltiples líneas que recogen la tabla de interés, y en las cuales los valores aparecen, de manera consecutiva, separados por el carácter ,.

Para importar este tipo de archivos en nuestra sesión de R, se utiliza la función read_csv(). Para acceder a su documentación utilizamos el comando ?read_csv.

El único argumento que debemos de pasar a esta función de manera obligatoria, es file, el nombre o la ruta completa del archivo que pretendemos importar.

library(readr)

read_csv(
  file,
  col_names = TRUE,
  col_types = NULL,
  locale = default_locale(),
  na = c("", "NA"),
  quoted_na = TRUE,
  quote = "\"",
  comment = "")

La paquetería readr fue desarrollada recientemente para lidiar con la lectura de archivos grandes rápidamente. El paquete proporciona reemplazos para funciones como read.table(), read.csv() entre otras. Esta paquetería proporciona funciones que suelen ser mucho más rápidas que las funciones base que proporciona R.

Ventajas de readr:

  • Por lo general, son mucho más rápidos (~ 10x) que sus funciones equivalentes.

  • Producen tibbles:

    • No convierten vectores de caracteres en factores.
    • No usan nombres de filas ni modifican los nombres de columnas.
  • Reproducibilidad

  • No convierte, automáticamente, las columnas con cadenas de caracteres a factores, como sí hacen por defecto las otras funciones base de R.

  • Reconoce ocho clases diferentes de datos (enteros, lógicos, etc.), dejando el resto como cadenas de caracteres.

Veamos un ejemplo:

La base de datos llamada AmesHousing contiene un conjunto de datos con información de la Oficina del Tasador de Ames utilizada para calcular los valores tasados para las propiedades residenciales individuales vendidas en Ames, Iowa, de 2006 a 2010. FUENTES: Ames, Oficina del Tasador de Iowa.

Pueden descargar los datos para la clase aquí

base <- read.csv("data/ames.csv") 
head(base, 2)
##                           MS_SubClass                MS_Zoning Lot_Frontage
## 1 One_Story_1946_and_Newer_All_Styles  Residential_Low_Density          141
## 2 One_Story_1946_and_Newer_All_Styles Residential_High_Density           80
##   Lot_Area Street           Alley          Lot_Shape Land_Contour Utilities
## 1    31770   Pave No_Alley_Access Slightly_Irregular          Lvl    AllPub
## 2    11622   Pave No_Alley_Access            Regular          Lvl    AllPub
##   Lot_Config Land_Slope Neighborhood Condition_1 Condition_2 Bldg_Type
## 1     Corner        Gtl   North_Ames        Norm        Norm    OneFam
## 2     Inside        Gtl   North_Ames       Feedr        Norm    OneFam
##   House_Style  Overall_Cond Year_Built Year_Remod_Add Roof_Style Roof_Matl
## 1   One_Story       Average       1960           1960        Hip   CompShg
## 2   One_Story Above_Average       1961           1961      Gable   CompShg
##   Exterior_1st Exterior_2nd Mas_Vnr_Type Mas_Vnr_Area Exter_Cond Foundation
## 1      BrkFace      Plywood        Stone          112    Typical     CBlock
## 2      VinylSd      VinylSd         None            0    Typical     CBlock
##   Bsmt_Cond Bsmt_Exposure BsmtFin_Type_1 BsmtFin_SF_1 BsmtFin_Type_2
## 1      Good            Gd            BLQ            2            Unf
## 2   Typical            No            Rec            6            LwQ
##   BsmtFin_SF_2 Bsmt_Unf_SF Total_Bsmt_SF Heating Heating_QC Central_Air
## 1            0         441          1080    GasA       Fair           Y
## 2          144         270           882    GasA    Typical           Y
##   Electrical First_Flr_SF Second_Flr_SF Gr_Liv_Area Bsmt_Full_Bath
## 1      SBrkr         1656             0        1656              1
## 2      SBrkr          896             0         896              0
##   Bsmt_Half_Bath Full_Bath Half_Bath Bedroom_AbvGr Kitchen_AbvGr TotRms_AbvGrd
## 1              0         1         0             3             1             7
## 2              0         1         0             2             1             5
##   Functional Fireplaces Garage_Type Garage_Finish Garage_Cars Garage_Area
## 1        Typ          2      Attchd           Fin           2         528
## 2        Typ          0      Attchd           Unf           1         730
##   Garage_Cond      Paved_Drive Wood_Deck_SF Open_Porch_SF Enclosed_Porch
## 1     Typical Partial_Pavement          210            62              0
## 2     Typical            Paved          140             0              0
##   Three_season_porch Screen_Porch Pool_Area Pool_QC           Fence
## 1                  0            0         0 No_Pool        No_Fence
## 2                  0          120         0 No_Pool Minimum_Privacy
##   Misc_Feature Misc_Val Mo_Sold Year_Sold Sale_Type Sale_Condition Sale_Price
## 1         None        0       5      2010       WD          Normal     215000
## 2         None        0       6      2010       WD          Normal     105000
##   Longitude Latitude
## 1 -93.61975 42.05403
## 2 -93.61976 42.05301
tidy <- read_csv("data/ames.csv")
head(tidy, 2)
## # A tibble: 2 × 74
##   MS_SubClass             MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##   <chr>                   <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
## 1 One_Story_1946_and_New… Resident…          141    31770 Pave   No_A… Slightly…
## 2 One_Story_1946_and_New… Resident…           80    11622 Pave   No_A… Regular  
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>,
## #   Bsmt_Exposure <chr>, BsmtFin_Type_1 <chr>, BsmtFin_SF_1 <dbl>, …

¿Y si el archivo que necesitamos leer esta en excel?

2.3.2 Archivos txt

Uno de los archivos más comunes es el .txt. La librería readr también cuenta con funciones que permiten leer fácilmente los datos contenidos en formato tabular.

ames_txt <- read_delim("data/ames.txt", delim = ";", col_names = TRUE)
head(ames_txt, 2)
## # A tibble: 2 × 74
##   MS_SubClass             MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##   <chr>                   <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
## 1 One_Story_1946_and_New… Resident…          141    31770 Pave   No_A… Slightly…
## 2 One_Story_1946_and_New… Resident…           80    11622 Pave   No_A… Regular  
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>,
## #   Bsmt_Exposure <chr>, BsmtFin_Type_1 <chr>, BsmtFin_SF_1 <dbl>, …

La función read_delim() funciona para leer archivos con diferentes delimitadores posibles, es decir, es posible especificar si las columnas están separadas por espacios, comas, punto y coma, tabulador o algún otro delimitador (““,”,“,”;“,”, “@”).

Adicionalmente, se puede especificar si el archivo contiene encabezado, si existen renglones a saltar, codificación, tipo de variable y muchas más opciones. Todos estos detalles pueden consultarse en la documentación de ayuda.

2.3.3 Archivos xls y xlsx

La paquetería readxl facilita la obtención de datos tabulares de archivos de Excel. Admite tanto el formato .xls heredado como el formato .xlsx moderno basado en XML.

Esta paquetería pone a disposición las siguientes funciones:

  • read_xlsx() lee un archivo con extensión xlsx.
read_xlsx(
 path,
 sheet = NULL,
 range = NULL,
 col_names = TRUE,
 col_types = NULL,
 na = "", 
 trim_ws = TRUE,
 skip = 0,
 n_max = Inf,
 guess_max = min(1000, n_max),
 progress = readxl_progress(),
 .name_repair = "unique"
 )
  • read_xls() lee un archivo con extensión xls.
read_xls(
 path,
 sheet = NULL,
 range = NULL,
 col_names = TRUE,
 col_types = NULL,
 na = "", 
 trim_ws = TRUE,
 skip = 0,
 n_max = Inf, 
 guess_max = min(1000, n_max),
 progress = readxl_progress(),
 .name_repair = "unique"
 )
  • read_excel() determina si el archivo es de tipo xls o xlsx para después llamar a una de las funciones mencionadas anteriormente.
read_excel(
 path,
 sheet = NULL,
 range = NULL,
 col_names = TRUE,
 col_types = NULL, 
 na = "",
 trim_ws = TRUE, 
 skip = 0,
 n_max = Inf,
 guess_max = min(1000, n_max),
 progress = readxl_progress(),
 .name_repair = "unique"
 )

EJERCICIO: Leer archivo excel de la carpeta del curso

2.3.4 Archivos json

Se utiliza la función fromJSON de la paquetería jsonlite

library(jsonlite)

base_json <- jsonlite::fromJSON("data/ames.json")
head(base_json, 2)
##                           MS_SubClass                MS_Zoning Lot_Frontage
## 1 One_Story_1946_and_Newer_All_Styles  Residential_Low_Density          141
## 2 One_Story_1946_and_Newer_All_Styles Residential_High_Density           80
##   Lot_Area Street           Alley          Lot_Shape Land_Contour Utilities
## 1    31770   Pave No_Alley_Access Slightly_Irregular          Lvl    AllPub
## 2    11622   Pave No_Alley_Access            Regular          Lvl    AllPub
##   Lot_Config Land_Slope Neighborhood Condition_1 Condition_2 Bldg_Type
## 1     Corner        Gtl   North_Ames        Norm        Norm    OneFam
## 2     Inside        Gtl   North_Ames       Feedr        Norm    OneFam
##   House_Style  Overall_Cond Year_Built Year_Remod_Add Roof_Style Roof_Matl
## 1   One_Story       Average       1960           1960        Hip   CompShg
## 2   One_Story Above_Average       1961           1961      Gable   CompShg
##   Exterior_1st Exterior_2nd Mas_Vnr_Type Mas_Vnr_Area Exter_Cond Foundation
## 1      BrkFace      Plywood        Stone          112    Typical     CBlock
## 2      VinylSd      VinylSd         None            0    Typical     CBlock
##   Bsmt_Cond Bsmt_Exposure BsmtFin_Type_1 BsmtFin_SF_1 BsmtFin_Type_2
## 1      Good            Gd            BLQ            2            Unf
## 2   Typical            No            Rec            6            LwQ
##   BsmtFin_SF_2 Bsmt_Unf_SF Total_Bsmt_SF Heating Heating_QC Central_Air
## 1            0         441          1080    GasA       Fair           Y
## 2          144         270           882    GasA    Typical           Y
##   Electrical First_Flr_SF Second_Flr_SF Gr_Liv_Area Bsmt_Full_Bath
## 1      SBrkr         1656             0        1656              1
## 2      SBrkr          896             0         896              0
##   Bsmt_Half_Bath Full_Bath Half_Bath Bedroom_AbvGr Kitchen_AbvGr TotRms_AbvGrd
## 1              0         1         0             3             1             7
## 2              0         1         0             2             1             5
##   Functional Fireplaces Garage_Type Garage_Finish Garage_Cars Garage_Area
## 1        Typ          2      Attchd           Fin           2         528
## 2        Typ          0      Attchd           Unf           1         730
##   Garage_Cond      Paved_Drive Wood_Deck_SF Open_Porch_SF Enclosed_Porch
## 1     Typical Partial_Pavement          210            62              0
## 2     Typical            Paved          140             0              0
##   Three_season_porch Screen_Porch Pool_Area Pool_QC           Fence
## 1                  0            0         0 No_Pool        No_Fence
## 2                  0          120         0 No_Pool Minimum_Privacy
##   Misc_Feature Misc_Val Mo_Sold Year_Sold Sale_Type Sale_Condition Sale_Price
## 1         None        0       5      2010       WD          Normal     215000
## 2         None        0       6      2010       WD          Normal     105000
##   Longitude Latitude
## 1  -93.6198   42.054
## 2  -93.6198   42.053

2.3.5 Archivos rds

Un tipo de archivo que resulta de particular interés, es el .RDS. Este archivo comprime cualquier objeto o resultado que sea usado o producido en R. Uno puede almacenar el objeto de interés de la siguiente manera:

saveRDS(base_json, "data/ames.rds")

Puede observarse que en el explorador de archivos se encuentra ahora el nuevo archivo con extensión .rds, el cual puede ser posteriormente incorporado a una sesión de R para seguir trabajando con él.

base_rds <- readRDS("data/ames.rds")

Algunas de las grandes ventajas que tiene almacenar los archivos en formato rds, son las siguientes:

  • No es necesario volver a ejecutar procesos largos cuando ya se ha logrado realizar una vez.

  • El tiempo de lectura de la información es considerablemente más rápido.

2.4 Consultas de datos

Ahora que ya se ha estudiado la manera de cargar datos, aprenderemos como manipularlos con dplyr. El paquete dplyr proporciona un conjunto de funciones muy útiles para manipular data-frames y así reducir el número de repeticiones, la probabilidad de cometer errores y el número de caracteres que hay que escribir. Como valor extra, podemos encontrar que la gramática de dplyr es más fácil de entender.

Revisaremos algunas de sus funciones más usadas (verbos), así como el uso de pipes (%>%) para combinarlas.

  • select()

  • filter()

  • arrange()

  • mutate()

  • summarise()

  • join()

  • group_by()

Primero tenemos que instalar y cargar la paquetería (parte de tidyverse):

# install.packages("dplyr")

library(dplyr)
library(readr)

Usaremos el dataset AmesHousing que se proporcionó en el capítulo anterior (el alumno puede hacer el ejercicio con datos propios)

ames_housing <- read_csv("data/ames.csv")

glimpse(ames_housing)
## Rows: 2,930
## Columns: 74
## $ MS_SubClass        <chr> "One_Story_1946_and_Newer_All_Styles", "One_Story_1…
## $ MS_Zoning          <chr> "Residential_Low_Density", "Residential_High_Densit…
## $ Lot_Frontage       <dbl> 141, 80, 81, 93, 74, 78, 41, 43, 39, 60, 75, 0, 63,…
## $ Lot_Area           <dbl> 31770, 11622, 14267, 11160, 13830, 9978, 4920, 5005…
## $ Street             <chr> "Pave", "Pave", "Pave", "Pave", "Pave", "Pave", "Pa…
## $ Alley              <chr> "No_Alley_Access", "No_Alley_Access", "No_Alley_Acc…
## $ Lot_Shape          <chr> "Slightly_Irregular", "Regular", "Slightly_Irregula…
## $ Land_Contour       <chr> "Lvl", "Lvl", "Lvl", "Lvl", "Lvl", "Lvl", "Lvl", "H…
## $ Utilities          <chr> "AllPub", "AllPub", "AllPub", "AllPub", "AllPub", "…
## $ Lot_Config         <chr> "Corner", "Inside", "Corner", "Corner", "Inside", "…
## $ Land_Slope         <chr> "Gtl", "Gtl", "Gtl", "Gtl", "Gtl", "Gtl", "Gtl", "G…
## $ Neighborhood       <chr> "North_Ames", "North_Ames", "North_Ames", "North_Am…
## $ Condition_1        <chr> "Norm", "Feedr", "Norm", "Norm", "Norm", "Norm", "N…
## $ Condition_2        <chr> "Norm", "Norm", "Norm", "Norm", "Norm", "Norm", "No…
## $ Bldg_Type          <chr> "OneFam", "OneFam", "OneFam", "OneFam", "OneFam", "…
## $ House_Style        <chr> "One_Story", "One_Story", "One_Story", "One_Story",…
## $ Overall_Cond       <chr> "Average", "Above_Average", "Above_Average", "Avera…
## $ Year_Built         <dbl> 1960, 1961, 1958, 1968, 1997, 1998, 2001, 1992, 199…
## $ Year_Remod_Add     <dbl> 1960, 1961, 1958, 1968, 1998, 1998, 2001, 1992, 199…
## $ Roof_Style         <chr> "Hip", "Gable", "Hip", "Hip", "Gable", "Gable", "Ga…
## $ Roof_Matl          <chr> "CompShg", "CompShg", "CompShg", "CompShg", "CompSh…
## $ Exterior_1st       <chr> "BrkFace", "VinylSd", "Wd Sdng", "BrkFace", "VinylS…
## $ Exterior_2nd       <chr> "Plywood", "VinylSd", "Wd Sdng", "BrkFace", "VinylS…
## $ Mas_Vnr_Type       <chr> "Stone", "None", "BrkFace", "None", "None", "BrkFac…
## $ Mas_Vnr_Area       <dbl> 112, 0, 108, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6…
## $ Exter_Cond         <chr> "Typical", "Typical", "Typical", "Typical", "Typica…
## $ Foundation         <chr> "CBlock", "CBlock", "CBlock", "CBlock", "PConc", "P…
## $ Bsmt_Cond          <chr> "Good", "Typical", "Typical", "Typical", "Typical",…
## $ Bsmt_Exposure      <chr> "Gd", "No", "No", "No", "No", "No", "Mn", "No", "No…
## $ BsmtFin_Type_1     <chr> "BLQ", "Rec", "ALQ", "ALQ", "GLQ", "GLQ", "GLQ", "A…
## $ BsmtFin_SF_1       <dbl> 2, 6, 1, 1, 3, 3, 3, 1, 3, 7, 7, 1, 7, 3, 3, 1, 3, …
## $ BsmtFin_Type_2     <chr> "Unf", "LwQ", "Unf", "Unf", "Unf", "Unf", "Unf", "U…
## $ BsmtFin_SF_2       <dbl> 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1120, 0…
## $ Bsmt_Unf_SF        <dbl> 441, 270, 406, 1045, 137, 324, 722, 1017, 415, 994,…
## $ Total_Bsmt_SF      <dbl> 1080, 882, 1329, 2110, 928, 926, 1338, 1280, 1595, …
## $ Heating            <chr> "GasA", "GasA", "GasA", "GasA", "GasA", "GasA", "Ga…
## $ Heating_QC         <chr> "Fair", "Typical", "Typical", "Excellent", "Good", …
## $ Central_Air        <chr> "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "…
## $ Electrical         <chr> "SBrkr", "SBrkr", "SBrkr", "SBrkr", "SBrkr", "SBrkr…
## $ First_Flr_SF       <dbl> 1656, 896, 1329, 2110, 928, 926, 1338, 1280, 1616, …
## $ Second_Flr_SF      <dbl> 0, 0, 0, 0, 701, 678, 0, 0, 0, 776, 892, 0, 676, 0,…
## $ Gr_Liv_Area        <dbl> 1656, 896, 1329, 2110, 1629, 1604, 1338, 1280, 1616…
## $ Bsmt_Full_Bath     <dbl> 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, …
## $ Bsmt_Half_Bath     <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ Full_Bath          <dbl> 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 2, …
## $ Half_Bath          <dbl> 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, …
## $ Bedroom_AbvGr      <dbl> 3, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 2, 1, 4, 4, …
## $ Kitchen_AbvGr      <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ TotRms_AbvGrd      <dbl> 7, 5, 6, 8, 6, 7, 6, 5, 5, 7, 7, 6, 7, 5, 4, 12, 8,…
## $ Functional         <chr> "Typ", "Typ", "Typ", "Typ", "Typ", "Typ", "Typ", "T…
## $ Fireplaces         <dbl> 2, 0, 0, 2, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, …
## $ Garage_Type        <chr> "Attchd", "Attchd", "Attchd", "Attchd", "Attchd", "…
## $ Garage_Finish      <chr> "Fin", "Unf", "Unf", "Fin", "Fin", "Fin", "Fin", "R…
## $ Garage_Cars        <dbl> 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, …
## $ Garage_Area        <dbl> 528, 730, 312, 522, 482, 470, 582, 506, 608, 442, 4…
## $ Garage_Cond        <chr> "Typical", "Typical", "Typical", "Typical", "Typica…
## $ Paved_Drive        <chr> "Partial_Pavement", "Paved", "Paved", "Paved", "Pav…
## $ Wood_Deck_SF       <dbl> 210, 140, 393, 0, 212, 360, 0, 0, 237, 140, 157, 48…
## $ Open_Porch_SF      <dbl> 62, 0, 36, 0, 34, 36, 0, 82, 152, 60, 84, 21, 75, 0…
## $ Enclosed_Porch     <dbl> 0, 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ Three_season_porch <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ Screen_Porch       <dbl> 0, 120, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 140, …
## $ Pool_Area          <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ Pool_QC            <chr> "No_Pool", "No_Pool", "No_Pool", "No_Pool", "No_Poo…
## $ Fence              <chr> "No_Fence", "Minimum_Privacy", "No_Fence", "No_Fenc…
## $ Misc_Feature       <chr> "None", "None", "Gar2", "None", "None", "None", "No…
## $ Misc_Val           <dbl> 0, 0, 12500, 0, 0, 0, 0, 0, 0, 0, 0, 500, 0, 0, 0, …
## $ Mo_Sold            <dbl> 5, 6, 6, 4, 3, 6, 4, 1, 3, 6, 4, 3, 5, 2, 6, 6, 6, …
## $ Year_Sold          <dbl> 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 201…
## $ Sale_Type          <chr> "WD", "WD", "WD", "WD", "WD", "WD", "WD", "WD", "WD…
## $ Sale_Condition     <chr> "Normal", "Normal", "Normal", "Normal", "Normal", "…
## $ Sale_Price         <dbl> 215000, 105000, 172000, 244000, 189900, 195500, 213…
## $ Longitude          <dbl> -93.61975, -93.61976, -93.61939, -93.61732, -93.638…
## $ Latitude           <dbl> 42.05403, 42.05301, 42.05266, 42.05125, 42.06090, 4…

2.4.1 Seleccionar columnas

Observamos que nuestros datos tienen 2,930 observaciones y 74 variables, con select() podemos seleccionar las variables que se indiquen.

ames_housing %>% select(Lot_Area, Neighborhood, Year_Sold, Sale_Price)
## # A tibble: 2,930 × 4
##    Lot_Area Neighborhood Year_Sold Sale_Price
##       <dbl> <chr>            <dbl>      <dbl>
##  1    31770 North_Ames        2010     215000
##  2    11622 North_Ames        2010     105000
##  3    14267 North_Ames        2010     172000
##  4    11160 North_Ames        2010     244000
##  5    13830 Gilbert           2010     189900
##  6     9978 Gilbert           2010     195500
##  7     4920 Stone_Brook       2010     213500
##  8     5005 Stone_Brook       2010     191500
##  9     5389 Stone_Brook       2010     236500
## 10     7500 Gilbert           2010     189000
## # ℹ 2,920 more rows

¡¡ RECORDAR !!

El operador pipe (%>%) se usa para conectar un elemento con una función o acción a realizar. En este caso solo se indica que en los datos de ames se seleccionan 4 variables.

Con select() y contains() podemos seleccionar variables con alguna cadena de texto.

ames_housing %>% select(contains("Area"))
## # A tibble: 2,930 × 5
##    Lot_Area Mas_Vnr_Area Gr_Liv_Area Garage_Area Pool_Area
##       <dbl>        <dbl>       <dbl>       <dbl>     <dbl>
##  1    31770          112        1656         528         0
##  2    11622            0         896         730         0
##  3    14267          108        1329         312         0
##  4    11160            0        2110         522         0
##  5    13830            0        1629         482         0
##  6     9978           20        1604         470         0
##  7     4920            0        1338         582         0
##  8     5005            0        1280         506         0
##  9     5389            0        1616         608         0
## 10     7500            0        1804         442         0
## # ℹ 2,920 more rows

De igual manera, con select(), ends_with y start_with() podemos seleccionar que inicien o terminen con alguna cadena de texto.

ames_housing %>% select(starts_with("Garage"))
## # A tibble: 2,930 × 5
##    Garage_Type Garage_Finish Garage_Cars Garage_Area Garage_Cond
##    <chr>       <chr>               <dbl>       <dbl> <chr>      
##  1 Attchd      Fin                     2         528 Typical    
##  2 Attchd      Unf                     1         730 Typical    
##  3 Attchd      Unf                     1         312 Typical    
##  4 Attchd      Fin                     2         522 Typical    
##  5 Attchd      Fin                     2         482 Typical    
##  6 Attchd      Fin                     2         470 Typical    
##  7 Attchd      Fin                     2         582 Typical    
##  8 Attchd      RFn                     2         506 Typical    
##  9 Attchd      RFn                     2         608 Typical    
## 10 Attchd      Fin                     2         442 Typical    
## # ℹ 2,920 more rows

Funciones útiles para select():

  • contains(): Selecciona variables cuyo nombre contiene la cadena de texto.

  • ends_with(): Selecciona variables cuyo nombre termina con la cadena de caracteres.

  • everything(): Selecciona todas las columnas.

  • matches(): Selecciona las variables cuyos nombres coinciden con una expresión regular.

  • num_range(): Selecciona las variables por posición.

  • start_with(): Selecciona variables cuyos nombres empiezan con la cadena de caracteres.

  • any_of: Selecciona cualquiera de estas variables, en caso de existir

EJERCICIO:

  • Crear con datos propios una consulta de columnas usando como variable auxiliar cada una de las listadas anteriormente. Será suficiente con realizar un ejemplo de cada una.

2.4.2 Filtrar observaciones

La función filter() nos permite filtrar filas según una condición, primero notemos que la variable Sale_Condition tiene distintas categorías.

table(ames_housing$Sale_Condition)
## 
## Abnorml AdjLand  Alloca  Family  Normal Partial 
##     190      12      24      46    2413     245

¡¡ SPOILER !!

En un modelo predictivo de Machine Learning, no es correcto agregar columnas cuyo valor es conocido hasta el momento de la observación. Es decir, no deben agregarse variables que no se conozca su valor al momento de la predicción, como es el caso de condición de venta.

Ahora usaremos la función filter para quedarnos solo con las observaciones con condición de venta “normal”.

ames_housing %>% filter(Sale_Condition == "Normal")
## # A tibble: 2,413 × 74
##    MS_SubClass            MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##    <chr>                  <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
##  1 One_Story_1946_and_Ne… Resident…          141    31770 Pave   No_A… Slightly…
##  2 One_Story_1946_and_Ne… Resident…           80    11622 Pave   No_A… Regular  
##  3 One_Story_1946_and_Ne… Resident…           81    14267 Pave   No_A… Slightly…
##  4 One_Story_1946_and_Ne… Resident…           93    11160 Pave   No_A… Regular  
##  5 Two_Story_1946_and_Ne… Resident…           74    13830 Pave   No_A… Slightly…
##  6 Two_Story_1946_and_Ne… Resident…           78     9978 Pave   No_A… Slightly…
##  7 One_Story_PUD_1946_an… Resident…           41     4920 Pave   No_A… Regular  
##  8 One_Story_PUD_1946_an… Resident…           43     5005 Pave   No_A… Slightly…
##  9 One_Story_PUD_1946_an… Resident…           39     5389 Pave   No_A… Slightly…
## 10 Two_Story_1946_and_Ne… Resident…           60     7500 Pave   No_A… Regular  
## # ℹ 2,403 more rows
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>, …

También se puede usar para filtrar variables numéricas:

ames_housing %>% filter(Lot_Area > 1000 & Sale_Price >= 150000)
## # A tibble: 1,677 × 74
##    MS_SubClass            MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##    <chr>                  <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
##  1 One_Story_1946_and_Ne… Resident…          141    31770 Pave   No_A… Slightly…
##  2 One_Story_1946_and_Ne… Resident…           81    14267 Pave   No_A… Slightly…
##  3 One_Story_1946_and_Ne… Resident…           93    11160 Pave   No_A… Regular  
##  4 Two_Story_1946_and_Ne… Resident…           74    13830 Pave   No_A… Slightly…
##  5 Two_Story_1946_and_Ne… Resident…           78     9978 Pave   No_A… Slightly…
##  6 One_Story_PUD_1946_an… Resident…           41     4920 Pave   No_A… Regular  
##  7 One_Story_PUD_1946_an… Resident…           43     5005 Pave   No_A… Slightly…
##  8 One_Story_PUD_1946_an… Resident…           39     5389 Pave   No_A… Slightly…
##  9 Two_Story_1946_and_Ne… Resident…           60     7500 Pave   No_A… Regular  
## 10 Two_Story_1946_and_Ne… Resident…           75    10000 Pave   No_A… Slightly…
## # ℹ 1,667 more rows
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>, …

Notemos que en el ejemplo anterior se usa &, que ayuda a filtrar por dos condiciones.

También puede usarse | para filtrar por alguna de las dos condiciones.

ames_housing %>% filter(Lot_Area < 1000 | Sale_Price <= 150000)
## # A tibble: 1,271 × 74
##    MS_SubClass            MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##    <chr>                  <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
##  1 One_Story_1946_and_Ne… Resident…           80    11622 Pave   No_A… Regular  
##  2 One_Story_1946_and_Ne… Resident…          140    19138 Pave   No_A… Regular  
##  3 One_Story_1946_and_Ne… Resident…            0    11241 Pave   No_A… Slightly…
##  4 One_Story_1946_and_Ne… Resident…            0    12537 Pave   No_A… Slightly…
##  5 One_Story_1946_and_Ne… Resident…           65     8450 Pave   No_A… Regular  
##  6 One_Story_1946_and_Ne… Resident…           70     8400 Pave   No_A… Regular  
##  7 One_Story_1946_and_Ne… Resident…           70    10500 Pave   No_A… Regular  
##  8 Two_Story_PUD_1946_an… Resident…           21     1680 Pave   No_A… Regular  
##  9 Two_Story_PUD_1946_an… Resident…           21     1680 Pave   No_A… Regular  
## 10 Two_Story_PUD_1946_an… Resident…           21     1680 Pave   No_A… Regular  
## # ℹ 1,261 more rows
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>, …

Las condiciones pueden ser expresiones lógicas construidas mediante los operadores relacionales y lógicos:

  • < : Menor que

  • > : Mayor que

  • == : Igual que

  • <= : Menor o igual que

  • >= : Mayor o igual que

  • != : Diferente que

  • %in% : Pertenece al conjunto

  • is.na : Es NA

  • !is.na : No es NA

EJERCICIO:

  • Practicar la función de filtro de observaciones usando los operadores auxiliares.

  • Concatenar el resultado de seleccionar columnas y posteriormente filtrar columnas.

2.4.3 Ordenar registros

La función arrange() se utiliza para ordenar las filas de un data frame de acuerdo a una o varias variables. Este ordenamiento puede ser ascendente o descendente.

Por defecto arrange() ordena las filas por orden ascendente:

ames_housing %>% arrange(Sale_Price)
## # A tibble: 2,930 × 74
##    MS_SubClass            MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##    <chr>                  <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
##  1 One_Story_1945_and_Ol… Resident…           68     9656 Pave   No_A… Regular  
##  2 One_Story_1946_and_Ne… A_agr               80    14584 Pave   No_A… Regular  
##  3 One_Story_1945_and_Ol… C_all               60     7879 Pave   No_A… Regular  
##  4 One_Story_1945_and_Ol… Resident…           60     8088 Pave   Grav… Regular  
##  5 One_Story_1946_and_Ne… C_all               50     9000 Pave   No_A… Regular  
##  6 One_and_Half_Story_Fi… Resident…           50     5925 Pave   No_A… Regular  
##  7 One_Story_1946_and_Ne… Resident…           50     5000 Pave   No_A… Regular  
##  8 Two_Story_1945_and_Ol… C_all               50     8500 Pave   Paved Regular  
##  9 One_Story_1945_and_Ol… C_all               72     9392 Pave   No_A… Regular  
## 10 One_Story_1945_and_Ol… Resident…           50     5925 Pave   No_A… Regular  
## # ℹ 2,920 more rows
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>, …



Si las queremos ordenar de forma ascendente, lo haremos del siguiente modo:

ames_housing %>% arrange(desc(Sale_Price))
## # A tibble: 2,930 × 74
##    MS_SubClass            MS_Zoning Lot_Frontage Lot_Area Street Alley Lot_Shape
##    <chr>                  <chr>            <dbl>    <dbl> <chr>  <chr> <chr>    
##  1 Two_Story_1946_and_Ne… Resident…          104    21535 Pave   No_A… Slightly…
##  2 Two_Story_1946_and_Ne… Resident…          160    15623 Pave   No_A… Slightly…
##  3 Two_Story_1946_and_Ne… Resident…          118    35760 Pave   No_A… Slightly…
##  4 One_Story_1946_and_Ne… Resident…          106    12720 Pave   No_A… Regular  
##  5 One_Story_1946_and_Ne… Resident…          100    12919 Pave   No_A… Slightly…
##  6 One_Story_1946_and_Ne… Resident…          105    13693 Pave   No_A… Regular  
##  7 One_Story_1946_and_Ne… Resident…           52    51974 Pave   No_A… Slightly…
##  8 Two_Story_1946_and_Ne… Resident…          114    17242 Pave   No_A… Slightly…
##  9 Two_Story_1946_and_Ne… Resident…          107    13891 Pave   No_A… Regular  
## 10 Two_Story_1946_and_Ne… Resident…           85    16056 Pave   No_A… Slightly…
## # ℹ 2,920 more rows
## # ℹ 67 more variables: Land_Contour <chr>, Utilities <chr>, Lot_Config <chr>,
## #   Land_Slope <chr>, Neighborhood <chr>, Condition_1 <chr>, Condition_2 <chr>,
## #   Bldg_Type <chr>, House_Style <chr>, Overall_Cond <chr>, Year_Built <dbl>,
## #   Year_Remod_Add <dbl>, Roof_Style <chr>, Roof_Matl <chr>,
## #   Exterior_1st <chr>, Exterior_2nd <chr>, Mas_Vnr_Type <chr>,
## #   Mas_Vnr_Area <dbl>, Exter_Cond <chr>, Foundation <chr>, Bsmt_Cond <chr>, …

Si se desea usar dos o más columnas para realizar el ordenamiento, deben separarse por comas cada una de las características

ames_housing %>% 
 arrange(Sale_Condition, desc(Sale_Price), Lot_Area) %>% 
 select(Sale_Condition, Sale_Price, Lot_Area)
## # A tibble: 2,930 × 3
##    Sale_Condition Sale_Price Lot_Area
##    <chr>               <dbl>    <dbl>
##  1 Abnorml            745000    15623
##  2 Abnorml            552000    14836
##  3 Abnorml            475000    11778
##  4 Abnorml            390000    13418
##  5 Abnorml            328900     5119
##  6 Abnorml            310000    14541
##  7 Abnorml            290000     9950
##  8 Abnorml            287000    15498
##  9 Abnorml            258000    12090
## 10 Abnorml            257000    10994
## # ℹ 2,920 more rows

Notemos que en el ejemplo anterior usamos dos pipes (%>%), como habíamos mencionado se pueden usar los necesarios para combinar funciones.

2.4.4 Agregar / Modificar

Con la función mutate() podemos computar transformaciones de variables en un data frame. A menudo, tendremos la necesidad de crear nuevas variables que se calculan a partir de variables existentes. La función mutate() proporciona una interfaz clara para realizar este tipo de operaciones.

Por ejemplo, haremos el cálculo de la antigüedad del inmueble a partir de las variables Year_Sold y Year_Remod_Add:

ejemplo_mutate <- ames_housing %>% 
 select(Year_Sold, Year_Remod_Add) %>%
 mutate(Antique = Year_Sold - Year_Remod_Add)

ejemplo_mutate
## # A tibble: 2,930 × 3
##    Year_Sold Year_Remod_Add Antique
##        <dbl>          <dbl>   <dbl>
##  1      2010           1960      50
##  2      2010           1961      49
##  3      2010           1958      52
##  4      2010           1968      42
##  5      2010           1998      12
##  6      2010           1998      12
##  7      2010           2001       9
##  8      2010           1992      18
##  9      2010           1996      14
## 10      2010           1999      11
## # ℹ 2,920 more rows

El ejemplo anterior crea una nueva variable. Ahora se presenta otro ejemplo en donde se modifica una variable ya creada.

ejemplo_mutate %>% 
 mutate(Antique = Antique * 12)
## # A tibble: 2,930 × 3
##    Year_Sold Year_Remod_Add Antique
##        <dbl>          <dbl>   <dbl>
##  1      2010           1960     600
##  2      2010           1961     588
##  3      2010           1958     624
##  4      2010           1968     504
##  5      2010           1998     144
##  6      2010           1998     144
##  7      2010           2001     108
##  8      2010           1992     216
##  9      2010           1996     168
## 10      2010           1999     132
## # ℹ 2,920 more rows

En este segundo ejemplo, se modifica el número de años de antigüedad y se multiplica por un factor de 12 para modificar el tiempo en una escala de meses.

2.4.5 Resumen estadístico

La función summarise() se comporta de forma análoga a la función mutate(), excepto que en lugar de añadir nuevas columnas crea un nuevo data frame.

Podemos usar el ejemplo anterior y calcular la media de la variable creada Antique:

ames_housing %>% 
 select(Year_Sold, Year_Remod_Add) %>%
 mutate(Antique = Year_Sold - Year_Remod_Add) %>%
 summarise(Mean_Antique = mean(Antique))
## # A tibble: 1 × 1
##   Mean_Antique
##          <dbl>
## 1         23.5

Solo fue necesario agregar un pipe, especificar el nombre de la variable creada y la operación a realizar.

A continuación se muestran funciones que trabajando conjuntamente con la función summarise() facilitarán nuestro trabajo diario. Las primeras pertenecen al paquete base y las otras son del paquete dplyr. Todas ellas toman como argumento un vector y devuelven un único resultado:

  • min(), max() : Valores max y min.

  • mean() : Media.

  • median() : Mediana.

  • sum() : Suma de los valores.

  • var(), sd() : Varianza y desviación estándar.

  • first() : Primer valor en un vector.

  • last() : El último valor en un vector

  • n() : El número de valores en un vector.

  • n_distinct() : El número de valores distintos en un vector.

  • nth() : Extrae el valor que ocupa la posición n en un vector.

Mas adelante veremos como combinar esta función con la función group_by() para calcular estadísticos agrupados por alguna característica de interés.

EJERCICIO:

  • Realizar una consulta usando summarise() y cada una de las funciones estadísticas listadas anteriormente.

2.4.6 Agrupamiento

La función group_by() agrupa un conjunto de filas de acuerdo con los valores de una o más columnas o expresiones.

Usaremos el ejemplo anterior. Primero creamos nuestra nueva variable Antique, después agrupamos por vecindario y al final calculamos la media de la variable Antique. Gracias al agrupamiento, nos regresara una media por cada grupo creado, es decir, nos regresara el promedio de la antigüedad por vecindario.

ames_housing %>% 
 mutate(Antique = Year_Sold - Year_Remod_Add) %>% 
 group_by(Neighborhood) %>% 
 summarise(Mean_Antique = round(mean(Antique), 0))
## # A tibble: 28 × 2
##    Neighborhood        Mean_Antique
##    <chr>                      <dbl>
##  1 Bloomington_Heights            2
##  2 Blueste                       25
##  3 Briardale                     35
##  4 Brookside                     39
##  5 Clear_Creek                   28
##  6 College_Creek                  8
##  7 Crawford                      29
##  8 Edwards                       33
##  9 Gilbert                        9
## 10 Green_Hills                   14
## # ℹ 18 more rows

¡¡ RECORDAR !!

En este link se encuentra un buen resumen de las funciones básicas de dplyr

2.5 Orden y estructura

Un conjunto de datos puede ser representado de muchas maneras distintas y contener en todos los casos la misma información. Sin embargo, no todos los modos en que se presenta la información resulta óptimo para su procesamiento y análisis. Los conjuntos de datos ordenados serán más fáciles de trabajar y analizar.

Algunas de las características principales que presentan los conjuntos de datos ordenados son las siguientes:

  1. Cada variable debe tener su propia columna.

  2. Cada observación debe tener su propio renglón.

  3. Cada valor debe tener su propia celda.

La figura anterior muestra la estructura de orden que debe tener un conjunto de datos. A pesar de que pueda parecer intuitivo y sencillo, en la práctica es considerable el número de conjuntos de datos desordenados. La limpieza y ordenamiento debe ser trabajado de forma impecable a fin de que puedan realizarse buenas prácticas. El tiempo de limpieza y ordenamiento varía mucho dependiendo de la dimensión del conjunto de datos.

Algunos de los principales problemas que pueden tener los conjuntos de datos no ordenados son:

  • Una variable puede estar dispersa en múltiples columnas
  • Una observación puede estar esparcida en múltiples renglones

La paquetería tidyr cuenta con funciones para resolver dichos problemas. Entre las principales funciones que tiene la paquetería, se encuentran pivot_longer(), pivot_wider(), separate() y unite(), mismas que se analizarán a continuación.

2.5.1 Pivote horizontal

La función pivot_wider() resulta muy útil a la hora de organizar los datos. Su función consiste en dispersar una variable clave en múltiples columnas.

Lo primero que se debe hacer para poder hacer uso de dicha función es instalar y cargar la librería.

El siguiente conjunto de datos contiene el número de localidades rurales y urbanas por municipio de la Ciudad de México. Como es posible observar, algunos municipios aparecen más de una vez en el marco de datos, esto se debe a que cada municipio puede tener ambos ámbitos, rural y urbano. Para hacer que el conjunto de datos sea ordenado, es necesario que cada observación aparezca una sola vez por renglón y cada una de las categorías (rural y urbano) de la variable “Ámbito” deberá ocupar el lugar de una columna.

El siguiente código muestra cómo convertir los datos no ordenados en un conjunto ordenado.

library(tidyr)

Resumen <- readRDS("data/loc_mun_cdmx.rds")

Resumen %>% pivot_wider(
  names_from = Ambito, 
  values_from =  Total_localidades
  )
## # A tibble: 16 × 3
##    NOM_MUN                Rural Urbano
##    <chr>                  <int>  <int>
##  1 Álvaro Obregón             3      1
##  2 La Magdalena Contreras     8      1
##  3 Cuajimalpa de Morelos     14      2
##  4 Tláhuac                   31      5
##  5 Xochimilco                78      1
##  6 Tlalpan                   95      4
##  7 Milpa Alta               187     10
##  8 Azcapotzalco              NA      1
##  9 Benito Juárez             NA      1
## 10 Coyoacán                  NA      1
## 11 Cuauhtémoc                NA      1
## 12 Gustavo A. Madero         NA      1
## 13 Iztacalco                 NA      1
## 14 Iztapalapa                NA      1
## 15 Miguel Hidalgo            NA      1
## 16 Venustiano Carranza       NA      1

En la tabla actual existe ahora un y solo un registro por cada observación (nombre de municipio en este caso). El valor que le corresponde a cada una de las columnas creadas es la frecuencia absoluta de localidades que tienen la característica “Rural” y “Urbano” respectivamente.

Pero… ¿qué pasa cuando no existen todos los valores en ambas columnas? Si no se especifica la manera de llenar los datos faltantes, estos contendrán NAs. Siempre se puede elegir el caracter o número con el cual se imputan los datos faltantes.

fish_encounters %>%
  pivot_wider(names_from = station, values_from = seen)
## # A tibble: 19 × 12
##    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE   MAW
##    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int> <int>
##  1 4842        1     1      1     1       1     1     1     1     1     1     1
##  2 4843        1     1      1     1       1     1     1     1     1     1     1
##  3 4844        1     1      1     1       1     1     1     1     1     1     1
##  4 4845        1     1      1     1       1    NA    NA    NA    NA    NA    NA
##  5 4847        1     1      1    NA      NA    NA    NA    NA    NA    NA    NA
##  6 4848        1     1      1     1      NA    NA    NA    NA    NA    NA    NA
##  7 4849        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
##  8 4850        1     1     NA     1       1     1     1    NA    NA    NA    NA
##  9 4851        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
## 10 4854        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
## 11 4855        1     1      1     1       1    NA    NA    NA    NA    NA    NA
## 12 4857        1     1      1     1       1     1     1     1     1    NA    NA
## 13 4858        1     1      1     1       1     1     1     1     1     1     1
## 14 4859        1     1      1     1       1    NA    NA    NA    NA    NA    NA
## 15 4861        1     1      1     1       1     1     1     1     1     1     1
## 16 4862        1     1      1     1       1     1     1     1     1    NA    NA
## 17 4863        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
## 18 4864        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
## 19 4865        1     1      1    NA      NA    NA    NA    NA    NA    NA    NA
fish_encounters %>%
  pivot_wider(names_from = station, values_from = seen, values_fill = 0)
## # A tibble: 19 × 12
##    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE   MAW
##    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int> <int>
##  1 4842        1     1      1     1       1     1     1     1     1     1     1
##  2 4843        1     1      1     1       1     1     1     1     1     1     1
##  3 4844        1     1      1     1       1     1     1     1     1     1     1
##  4 4845        1     1      1     1       1     0     0     0     0     0     0
##  5 4847        1     1      1     0       0     0     0     0     0     0     0
##  6 4848        1     1      1     1       0     0     0     0     0     0     0
##  7 4849        1     1      0     0       0     0     0     0     0     0     0
##  8 4850        1     1      0     1       1     1     1     0     0     0     0
##  9 4851        1     1      0     0       0     0     0     0     0     0     0
## 10 4854        1     1      0     0       0     0     0     0     0     0     0
## 11 4855        1     1      1     1       1     0     0     0     0     0     0
## 12 4857        1     1      1     1       1     1     1     1     1     0     0
## 13 4858        1     1      1     1       1     1     1     1     1     1     1
## 14 4859        1     1      1     1       1     0     0     0     0     0     0
## 15 4861        1     1      1     1       1     1     1     1     1     1     1
## 16 4862        1     1      1     1       1     1     1     1     1     0     0
## 17 4863        1     1      0     0       0     0     0     0     0     0     0
## 18 4864        1     1      0     0       0     0     0     0     0     0     0
## 19 4865        1     1      1     0       0     0     0     0     0     0     0

Ejercicio:

  1. Realiza un pivote horizontal a través del ámbito y el total de localidades.

  2. Rellena los datos faltantes con ceros.

En caso de que existan múltiples columnas que se desean dispersar mediante el pivote de una columna con múltiples categorías, es posible especificar tal re estructuración a través del siguiente código.

us_rent_income %>% arrange(NAME)
## # A tibble: 104 × 5
##    GEOID NAME       variable estimate   moe
##    <chr> <chr>      <chr>       <dbl> <dbl>
##  1 01    Alabama    income      24476   136
##  2 01    Alabama    rent          747     3
##  3 02    Alaska     income      32940   508
##  4 02    Alaska     rent         1200    13
##  5 04    Arizona    income      27517   148
##  6 04    Arizona    rent          972     4
##  7 05    Arkansas   income      23789   165
##  8 05    Arkansas   rent          709     5
##  9 06    California income      29454   109
## 10 06    California rent         1358     3
## # ℹ 94 more rows
us_rent_income %>%
  pivot_wider(names_from = variable, values_from = c(estimate, moe))
## # A tibble: 52 × 6
##    GEOID NAME                 estimate_income estimate_rent moe_income moe_rent
##    <chr> <chr>                          <dbl>         <dbl>      <dbl>    <dbl>
##  1 01    Alabama                        24476           747        136        3
##  2 02    Alaska                         32940          1200        508       13
##  3 04    Arizona                        27517           972        148        4
##  4 05    Arkansas                       23789           709        165        5
##  5 06    California                     29454          1358        109        3
##  6 08    Colorado                       32401          1125        109        5
##  7 09    Connecticut                    35326          1123        195        5
##  8 10    Delaware                       31560          1076        247       10
##  9 11    District of Columbia           43198          1424        681       17
## 10 12    Florida                        25952          1077         70        3
## # ℹ 42 more rows

Ejercicio:

  1. Agrupa los datos de localidades por ámbito

  2. Agrega una columna con el porcentaje de localidades por alcaldía

  3. Realiza un pivote horizontal sobre el ámbito y las variables numéricas de total de localidades y su respectivo porcentaje creado en el paso anterior

  4. Ordena los registros de forma descendente de acuerdo con el total de localidades rural y urbano.

Adicionalmente, se puede especificar una función de agregación que operara antes de acomodar los datos en las respectivas columnas indicadas. Un ejemplo de funciones agregadas en la re estructuración de tabla se muestra a continuación, donde se muestra la media de los valores en las categorías tension y breaks.

warpbreaks <- warpbreaks[c("wool", "tension", "breaks")] %>% as_tibble()
warpbreaks
## # A tibble: 54 × 3
##    wool  tension breaks
##    <fct> <fct>    <dbl>
##  1 A     L           26
##  2 A     L           30
##  3 A     L           54
##  4 A     L           25
##  5 A     L           70
##  6 A     L           52
##  7 A     L           51
##  8 A     L           26
##  9 A     L           67
## 10 A     M           18
## # ℹ 44 more rows
warpbreaks %>%
  pivot_wider(
    names_from = wool,
    values_from = breaks,
    values_fn = ~mean(.x, na.rm = T)
  )
## # A tibble: 3 × 3
##   tension     A     B
##   <fct>   <dbl> <dbl>
## 1 L        44.6  28.2
## 2 M        24    28.8
## 3 H        24.6  18.8

Ejercicio:

  1. Sobre el conjunto de localidades crea una variable con 5 categorías numéricas creadas aleatoriamente.

  2. Elimina la columna con el nombre del municipio.

  3. Crea un pivote horizontal con el ámbito, sumando el total de localidades y rellenando con ceros los datos faltantes.

  4. Ordena las categorías numéricas de forma ascendente.

2.5.2 Pivote vertical

pivot_longer() es podría ser la función inversa de la anterior, se necesita comúnmente para ordenar los conjuntos de datos capturados en crudo, ya que a menudo no son capturados acorde a las mejores estructuras para facilitar el análisis.

El conjunto de datos relig_income almacena recuentos basados en una encuesta que (entre otras cosas) preguntó a las personas sobre su religión e ingresos anuales:

relig_income
## # A tibble: 18 × 11
##    religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` `$75-100k`
##    <chr>      <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>      <dbl>
##  1 Agnostic      27        34        60        81        76       137        122
##  2 Atheist       12        27        37        52        35        70         73
##  3 Buddhist      27        21        30        34        33        58         62
##  4 Catholic     418       617       732       670       638      1116        949
##  5 Don’t k…      15        14        15        11        10        35         21
##  6 Evangel…     575       869      1064       982       881      1486        949
##  7 Hindu          1         9         7         9        11        34         47
##  8 Histori…     228       244       236       238       197       223        131
##  9 Jehovah…      20        27        24        24        21        30         15
## 10 Jewish        19        19        25        25        30        95         69
## 11 Mainlin…     289       495       619       655       651      1107        939
## 12 Mormon        29        40        48        51        56       112         85
## 13 Muslim         6         7         9        10         9        23         16
## 14 Orthodox      13        17        23        32        32        47         38
## 15 Other C…       9         7        11        13        13        14         18
## 16 Other F…      20        33        40        46        49        63         46
## 17 Other W…       5         2         3         4         2         7          3
## 18 Unaffil…     217       299       374       365       341       528        407
## # ℹ 3 more variables: `$100-150k` <dbl>, `>150k` <dbl>,
## #   `Don't know/refused` <dbl>

¿Crees que ésta es la mejor estructura para la tabla? ¿Cómo imaginas que podría modificarse?

Este conjunto de datos contiene tres variables:

  1. religión, almacenada en las filas

  2. income repartidos entre los nombres de columna

  3. count almacenado en los valores de las celdas.

Para ordenarlo usamos pivot_longer():

relig_income %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")
## # A tibble: 180 × 3
##    religion income             count
##    <chr>    <chr>              <dbl>
##  1 Agnostic <$10k                 27
##  2 Agnostic $10-20k               34
##  3 Agnostic $20-30k               60
##  4 Agnostic $30-40k               81
##  5 Agnostic $40-50k               76
##  6 Agnostic $50-75k              137
##  7 Agnostic $75-100k             122
##  8 Agnostic $100-150k            109
##  9 Agnostic >150k                 84
## 10 Agnostic Don't know/refused    96
## # ℹ 170 more rows
  • El primer argumento es el conjunto de datos para remodelar, relig_income.

  • El segundo argumento describe qué columnas necesitan ser reformadas. En este caso, es cada columna aparte de religion.

  • El names_to da el nombre de la variable que se creará a partir de los datos almacenados en los nombres de columna, es decir, ingresos.

  • Los values_to dan el nombre de la variable que se creará a partir de los datos almacenados en el valor de la celda, es decir, count. Ni la columna names_to ni la values_to existen en relig_income, por lo que las proporcionamos como cadenas de caracteres entre comillas.

2.5.3 Unión de columnas

Es común que en los conjuntos de datos exista información esparcida en distintas columnas que sería deseable (en muy pocas ocasiones) tenerlas en una sola columna. Algunos ejemplos de esta situación deseable son las fechas y claves geoestadísticas. La función unite() sirve para concatenar el contenido de columnas mediante un separador elegible.

Se usará la variable de la clave geoestadística de localidades del país como ejemplo.

El formato para las claves geoestadísticas para estado, municipio y localidad son claves alfanuméricas que contienen 2, 3 y 4 caracteres respectivamente. Es indispensable que al trabajar con claves geoestadísticas, las claves estén en su formato original. A continuación se hará la homologación de las claves para usar la función unite().

library(magrittr)
library(readxl)
library(stringr)

Datos <- read_excel("data/Margin CONAPO.xlsx", sheet = "Margin CONAPO")
Datos
## # A tibble: 107,458 × 21
##      ENT NOM_ENT         MUN NOM_MUN   LOC NOM_LOC POB_TOT    VPH ANAL10 SPRIM10
##    <dbl> <chr>         <dbl> <chr>   <dbl> <chr>     <dbl>  <dbl>  <dbl>   <dbl>
##  1     1 Aguascalient…     1 Aguasc…     1 Aguasc…  722250 184123   2.26   10.9 
##  2     1 Aguascalient…     1 Aguasc…    96 Agua A…      37     11  17.9    48.1 
##  3     1 Aguascalient…     1 Aguasc…   104 Ardill…      14      3   0      20   
##  4     1 Aguascalient…     1 Aguasc…   106 Arella…    1382    255   5.60   24.7 
##  5     1 Aguascalient…     1 Aguasc…   112 Bajío …      55     11  14.3    38.1 
##  6     1 Aguascalient…     1 Aguasc…   114 Reside…     757    202   0       1.63
##  7     1 Aguascalient…     1 Aguasc…   120 Buenav…     935    217  10.7    29.5 
##  8     1 Aguascalient…     1 Aguasc…   121 Cabeci…     184     44   4.55   32.6 
##  9     1 Aguascalient…     1 Aguasc…   125 Cañada…     395     82   8.86   23.9 
## 10     1 Aguascalient…     1 Aguasc…   126 Cañada…     509    123   4.75   19.6 
## # ℹ 107,448 more rows
## # ℹ 11 more variables: SEXC10 <dbl>, SEE10 <dbl>, SAGUAE10 <dbl>,
## #   PROM_OCC10 <dbl>, PISOTIE10 <dbl>, SREFRI10 <dbl>, IM_2010 <dbl>,
## #   GM_2010 <chr>, IMC0A100 <dbl>, LUG_NAL <dbl>, LUG_EDO <dbl>

Como puede apreciarse en la tabla anterior, las claves de los campos Ent, Mun y Loc aparecen como numéricos. La estructura deseada para estos campos es de tipo alfanumérico y de longitud 2, 3 y 4 respectivamente. Para lograr esta estructura de datos, es necesario concatenar tantos ceros como sean necesarios antes del valor actual hasta lograr la longitud deseada.

Datos2 <- Datos %>% select(ENT, MUN, LOC)
Datos2$ENT %<>% str_pad(width = 2, side = "left", pad = "0")
Datos2$MUN %<>% str_pad(width = 3, side = "left", pad = "0")
Datos2$LOC %<>% str_pad(width = 4, side = "left", pad = "0")

Datos2 %>% head(5)
## # A tibble: 5 × 3
##   ENT   MUN   LOC  
##   <chr> <chr> <chr>
## 1 01    001   0001 
## 2 01    001   0096 
## 3 01    001   0104 
## 4 01    001   0106 
## 5 01    001   0112
Datos2 %>% 
 unite("CVE_GEO", c("ENT","MUN","LOC"), sep="", remove = F) %>% 
 head(5)
## # A tibble: 5 × 4
##   CVE_GEO   ENT   MUN   LOC  
##   <chr>     <chr> <chr> <chr>
## 1 010010001 01    001   0001 
## 2 010010096 01    001   0096 
## 3 010010104 01    001   0104 
## 4 010010106 01    001   0106 
## 5 010010112 01    001   0112
Datos2 %>% 
 unite("CVE_GEO", c("ENT","MUN","LOC"), sep="/",remove = T) %>% 
 head(5)
## # A tibble: 5 × 1
##   CVE_GEO    
##   <chr>      
## 1 01/001/0001
## 2 01/001/0096
## 3 01/001/0104
## 4 01/001/0106
## 5 01/001/0112

En el código anterior se carga la librería magrittr para poder hacer uso del operador pipe doble “%<>%”, que permite al igual que el operador pipe simple “%>%”, usar como argumento al primer elemento y mandarlo hacia la función definida, además de guardar el resultado final de la cadena de pipes en el argumento original que fue usado como insumo para la función. Es importante tener en cuenta que el dato será reescrito y no se podrá tener acceso a su información almacenada antes de ser usado el operador.

Es opción del programador poder eliminar las variables originales que crearon la nueva variable o mantenerlas en el conjunto de datos. Esta opción está disponible en el parámetro remove de la función unite().

2.5.4 Separador de columnas

Los procesos que se han visto hasta ahora han tenido cada uno una función inversa, este es también el caso de la función unite que tiene por objetivo unir dos o más columnas en una. La función separate() separará una columna en dos o más dependiendo de la longitud que tenga y de las especificaciones de separación.

Datos_unite1 <- Datos2 %>% unite("CVE_GEO", c("ENT","MUN","LOC"), sep = "", remove = T) 
Datos_unite1 %>% head(5) 
## # A tibble: 5 × 1
##   CVE_GEO  
##   <chr>    
## 1 010010001
## 2 010010096
## 3 010010104
## 4 010010106
## 5 010010112
Datos_unite1 %>% 
  separate("CVE_GEO", c("EDO","MUNI","LOC"), sep = c(2, 5), remove=F) %>% 
  head(5)
## # A tibble: 5 × 4
##   CVE_GEO   EDO   MUNI  LOC  
##   <chr>     <chr> <chr> <chr>
## 1 010010001 01    001   0001 
## 2 010010096 01    001   0096 
## 3 010010104 01    001   0104 
## 4 010010106 01    001   0106 
## 5 010010112 01    001   0112

Ya sea que se le especifique el número de caracteres que debe de contar para hacer un corte o que se le indique qué caracter debe identificar para hacer la separación, la función separate() puede dividir la columna indicada y crear nuevas a partir de la original.