¿Qué son los marcos de datos?

El marco de datos, o conocido en ingles como “data frame”, es una forma en la cual podemos almacenar datos tabulares, similar a una tabla de una hoja de cálculo o una base de datos. Es una forma estructurada de organizar la información en filas y columnas, donde cada columna puede contener diferentes tipos de datos, como numéricos, caracteres, factores, booleanos, etc., y cada fila representa una entrada individual en el conjunto de datos.

Los marcos de datos pueden contener datos de una sola naturaleza (como los vectores o las matrices) o de diversas naturalezas (como las listas o los “data frame”) que nos representa las características de un fenómeno en general.

Características de un Marco de Datos:

  1. Heterogeneidad: Cada columna puede contener un tipo de dato diferente, lo que permite tener variables de distintos tipos (por ejemplo, una columna numérica y otra de texto).

  2. Etiquetas: Tanto las filas como las columnas pueden tener nombres, lo que facilita la manipulación y acceso a los datos.

  3. Operaciones sencillas: R tiene muchas funciones optimizadas para manipular y analizar marcos de datos, como subset(), merge(), apply(), dplyr, tidyverse, entre otros.

Tipos de naturaleza de los datos:

Los datos en general pueden ser clasificados en de tipo caracter que son aquellos que excluyen los números convencionales, de tipo numérico que son aquellos que comprender a los números reales, y de tipo lógico que comprende las decisiones de FALSO Y TRUE, existen subvariantes de estos que los veremos después.

Como se puede observar, los marcos de datos son la base fundamental para el análisis de datos en R Studios, ya que apartar de estos podemos manipular los datos para la generación de modelos matemáticos, sectorizar cierto tipo de población, la realización de pruebas de hipótesis, y la resolución de conclusiones etc.

Para poder iniciar, primero debemos saber algunas configuraciones básicas de R.

Lectura de datos:

La lectura de datos en R se puede realizar de múltiples formas, la forma mas sencilla, aunque solo nos podría servir para practicar la manipulación de datos, se puede realizar con el comando data(), con el cual podremos cargar una base de datos ya sea del propio R, o de un paquete aledaño.

Para el siguiente caso, necesitaremos el paquete de tidyverse para importar una base de datos con la cual empezaremos a trabajar:

library("tidyverse")
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Para realizar nuestra primera lectura de datos, se realiza con el siguiente comando:

data("diamonds") 

Así se cargará un conjunto de datos pre-cargados en R, para luego poder utilizarlos de manera efectiva.

Por otro lado, en la mayoría de los casos tendremos que importar nuestras bases de datos desde programas externos. Para lograr esto, el proceso suele ser más complicado; lo más básico es utilizar la función read.table(), que nos ayudará a importar la mayoría de las bases de datos.

Es recomendable crear un proyecto en R para evitar complicaciones al importar bases de datos. También puede funcionar colocar los archivos donde R almacena los datos por defecto. Finalmente, otra opción es copiar directamente la ruta del archivo, aunque esta puede ser un poco más engorrosa, sigue siendo una alternativa viable.

#La importación de datos se realiza de la siguiente manera: 
Inmigración <- read.table("C:/Users/Zack_/OneDrive/R-Proyects/Archivos/inmigintnalpry.csv", header = TRUE, 
            sep = ",", 
            fileEncoding = "latin1", 
            nrow = 100)

#Para conocer mas de la función read.table() se realiza con:
help("read.table")
## starting httpd help server ... done

Una forma mas fácil de importar los datos que queremos es mediante la siguiente función:

#datos <- read.table(file.choose())

De esta forma, nos será posible importar nuestras bases de datos. Por otra parte, existen paquetes para tipos de archivos específicos, en los casos donde el separador no es un espacio (” “):

  • .csv (valores separados por coma): Un archivo CSV es un archivo de texto sin formato que contiene una lista de datos. En su mayoría, utiliza comas para separar (o delimitar) los datos, aunque a veces se usan otros caracteres, como el punto y coma.

  • .tsv (valores separados por tabuladores): Un archivo .tsv almacena una tabla de datos en la que las columnas están separadas por tabuladores. Por ejemplo, una tabla de datos de una base de datos o los datos de una hoja de cálculo.

  • .fwf (archivos de ancho fijo): Un archivo .fwf tiene un formato específico que permite guardar datos textuales de forma organizada.

  • .log: Un archivo .log es generado por la computadora y registra los eventos de los sistemas operativos y otros programas de software.

Las funciones equivalentes en readr suelen ser mucho más rápidas que la función base de R, aunque estas no están en el R base, sino en el paquete tidyverse, y son las siguientes:

#Archivos de valores separados por comas (.csv):
Gasto_G <- read_csv("C:/Users/Zack_/OneDrive/R-Proyects/Clase de R/base_gasto.csv")
## Rows: 213 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (3): i, gm, im
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#Archivos de valores separados por tabulados:
## read_tsv() 

#Archivos delimitados en general:
## read_delim() 

#Archivos de ancho fijo:
## read_fwf() 

#Archivos tabulares en los que las columnas están 
#separadas por espacios en blanco:
## read_table(): 

#Archivos de registro web: 
## read_log(): 

Otra paqueteria que nos ayudara exclusivamente para leer archivos de excel es readxl, con esta paqueteria existe una multitud de ejemplos con los que podemos practicar como el siguiente:

library(readxl)

#Con este comando podemos cargar los ejemplos:
readxl_example()
##  [1] "clippy.xls"    "clippy.xlsx"   "datasets.xls"  "datasets.xlsx"
##  [5] "deaths.xls"    "deaths.xlsx"   "geometry.xls"  "geometry.xlsx"
##  [9] "type-me.xls"   "type-me.xlsx"

La función a utilizar para cargar nuestra base de datos es atravez de read_excel(), utilizada para archivos con terminación .xlxs y también para leer un archivo tipo .csv.

Michel_K <- read_excel("C:/Users/Zack_/OneDrive/R-Proyects/Archivos/Modelo_Kaleckiano.xlsx")

head(Michel_K)

Como se puede observar, la función read_excel genera un tibble, en lugar de un data frame:

class(Michel_K)
## [1] "tbl_df"     "tbl"        "data.frame"
#Para conocer mas información puedes usar el signo de 
#"?" y después la función: 
?read_excel

Agilizar la carga:

Una forma efectiva de agilizar el proceso de carga de nuestra base de datos (y conocer la naturaleza de las variables) en R es la siguiente:

  1. Primero, sería delimitar el número de renglones que queremos que R lea:
Inmigración <- read.table("C:/Users/Zack_/OneDrive/R-Proyects/Archivos/inmigintnalpry.csv", header = TRUE, 
             sep =",", 
             fileEncoding = "latin1", 
             nrow = 100)
  1. Después, usa la función sapply enviándole como parámetros el objeto creado y la función class():
clases <- sapply(Inmigración, class)
  1. Así, si invocamos el objeto clases, será posible conocer la naturaleza de los datos que contiene.
clases
##     renglon         año         ent      id_ent      cvegeo        sexo 
##   "integer"   "integer" "character"   "integer"   "integer" "character" 
##        edad inmigintnal 
##   "integer"   "numeric"
  1. Por último, conociendo ya la naturaleza de cada columna, será posible optimizar la lectura del archivo mediante la función colClasses:
Inmigración <- read.table("C:/Users/Zack_/OneDrive/R-Proyects/Archivos/inmigintnalpry.csv", header=TRUE, 
             sep=",", 
             fileEncoding = "latin1", 
             colClasses=clases)

Guardado de Objetos:

Finalmente, la forma de guardar los objetos que invocamos es mediante la función save(). De esta manera, podemos almacenar los objetos que deseemos en un único archivo. Para esto, debemos conocer el directorio del archivo, así como asignarle un nombre al archivo en sí.

Podremos conocer los objetos, variables y/o gráficas que generamos utilizando la función ls(), y eliminarlos con la función rm():

ls()
## [1] "clases"      "diamonds"    "Gasto_G"     "Inmigración" "Michel_K"
#Podremos eliminar aquello que no queremos mediante esta función:
rm(clases)

Conociendo esto, podemos elegir las variables que queremos guardar con la función save():

save(Inmigración, Gasto_G, file="Objetos_Practica.RData")

Finalmente, para cargar los datos que hemos guardado se realiza con esta función:

load("Objetos_Practica.RData")

Igualmente podremos guardar directamente en distintos formatos las bases que hemos modificado, mediante esta función:

write.csv(Gasto_G, file="Gasto_G.csv") 

Tipos de Objetos:

En R existe una gran variedad de objetos con los cuales podemos almacenar, manipular y realizar operaciones con cada una de las bases de datos que tenemos, con el objetivo de su posterior análisis. A continuación, veremos la base de todos los objetos que existen en R.

Vectores

Los vectores son la estructura base de R, ya que son la primera forma en la cual podemos almacenar nuestros datos. Estos suelen contener datos de una sola naturaleza, aunque a partir de ellos podemos crear otros objetos que contengan variables de distintas características.

La forma primitiva de crear vectores es mediante la función vector(). Estos deben contener solamente datos de una misma naturaleza y se crean de la siguiente forma:

#Vectores numéricos (Valores numéricos): 
vector("numeric", length = 10)  
##  [1] 0 0 0 0 0 0 0 0 0 0
#Vectores tipo carácter (Caracteres entre comillas ""):
vector("character", length = 10) 
##  [1] "" "" "" "" "" "" "" "" "" ""
#Vectores logicos (Falso y Verdadero)
vector("logical", length = 10) 
##  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

Existe otra forma de crear vectores, y es utilizando la función c(), colocando los datos directamente dentro del paréntesis. En este caso, la única condición es que los datos sean de la misma naturaleza. Los casos son los siguientes:

#Logical = Si es verdadero o Falso.
I_dont_Know <- c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)
print(I_dont_Know)
## [1]  TRUE FALSE  TRUE FALSE  TRUE FALSE
class(I_dont_Know)
## [1] "logical"
Je_ne_sais_pa <- c(T, F, T, F, T, F)
print(Je_ne_sais_pa)
## [1]  TRUE FALSE  TRUE FALSE  TRUE FALSE
class(Je_ne_sais_pa) 
## [1] "logical"
#Character = Si es Cualitativo.
Parle_French <- c("Areli","Artemisa","Cinthia","Mayte")
print(Parle_French)
## [1] "Areli"    "Artemisa" "Cinthia"  "Mayte"
class(Parle_French)
## [1] "character"
#Integer = Si es Cuantitativo y entero.
Real_Enter <- c(1L,2L,3L,4L)
print(Real_Enter)
## [1] 1 2 3 4
class(Real_Enter)
## [1] "integer"
#Real = Si es Cuantitativo y un Numero Real.
Frac_Raz <- c(1.1, 2.2, 3.3, 4.4) 
print(Frac_Raz)
## [1] 1.1 2.2 3.3 4.4
class(Frac_Raz)
## [1] "numeric"
#Complex = Si es un numero imaginario.
Img <- c(2+1i, 2+2i, 2+3i) 
print(Img)
## [1] 2+1i 2+2i 2+3i
class(Img)
## [1] "complex"

Ya teniendo nuestros vectores, podemos conocer sus propiedades mediante diversas funciones. Estas funciones no solo nos servirán para los vectores, sino también para otros tipos de objetos, con los cuales podremos identificar características básicas del objeto que queremos analizar.

#Saber cual es la naturaleza exacta del vector que queremos conocer: 
typeof(Img) 
## [1] "complex"
#Saber a cual clase (naturaleza) es a la que pertenece nuestro vector.
class(Parle_French)
## [1] "character"
#Conocer el num de elementos del vector
length(Je_ne_sais_pa) 
## [1] 6

A partir de esto, podemos construir variables y sus propiedades básicas, para posteriormente convertirlas de distintas maneras dependiendo nuestras necesidades.

Conversión:

La conversión mas básica es la de agregar nombres a cada uno de los datos que están contenidos en nuestro objeto. Esto se realiza de una forma sencilla, de la siguiente manera:

#Nombrar elementos de vectores:
names(Frac_Raz)= c("Real", "Positivo", "Fraccionario", "Racional") 

head(Frac_Raz)
##         Real     Positivo Fraccionario     Racional 
##          1.1          2.2          3.3          4.4

Por otra parte, existen vectores con múltiples datos de distintas naturalezas. Esto se puede corregir con la función complex(). Esta función puede convertir los valores de un vector con múltiples elementos a un solo elemento, como en el siguiente caso:

complex <- c(T, 19, 1+3i) 
print(complex)
## [1]  1+0i 19+0i  1+3i

Siempre que sea compatible, R puede homogeneizar la naturaleza de los valores que le damos.

La conversión puede suceder de distintas formas. Una de ellas es especificando exactamente qué clase de elemento queremos mediante distintas funciones, las cuales son las siguientes:

#Creación del vector del ejemplo.
element <- vector(mode = "numeric",length =5) 
head(element)
## [1] 0 0 0 0 0
#Vector Logico.
as.logical(element) 
## [1] FALSE FALSE FALSE FALSE FALSE
#Vector Numerico.
as.numeric(element) 
## [1] 0 0 0 0 0
#Vector Caracteristica.
as.character(element) 
## [1] "0" "0" "0" "0" "0"

Factores

El factor muestra la información con ligeros cambios al del vector común, en estos las comillas no son mostradas y que los niveles son explicitamente impresos.

La forma de convertir vectores a Factores es la siguiente:

factor(element)
## [1] 0 0 0 0 0
## Levels: 0

Búsqueda de elementos:

La búsqueda de elementos nos puede ayudar a localizar un elemento o un cierto rango de elementos de un vector. Esto se puede realizar de distintas formas, la más básica es la siguiente:

head(Parle_French)
## [1] "Areli"    "Artemisa" "Cinthia"  "Mayte"

Esto se realiza mediante corchetes [], donde fuera del paréntesis va el nombre de nuestro vector, y dentro del corchete ponemos el número del dato que queremos invocar y guardar:

Medicina <- Parle_French[3]
head(Medicina)
## [1] "Cinthia"

Esta es la forma en la que podemos invocar un objeto, pero no la única, ya que podemos invocar varios objetos a la vez con una misma función:

mi_vector <- 20:50
  • Nos invoca los elementos dentro del intervalo:
mi_vector[1:5]
## [1] 20 21 22 23 24
  • Nos invoca los elementos fuera del intervalo:
mi_vector[-9:-15]
##  [1] 20 21 22 23 24 25 26 27 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
  • Invoca los elementos que exactamente le pedimos, sin importar el orden de los números:
Parle_French[c(1, 2, 4)]
## [1] "Areli"    "Artemisa" "Mayte"
  • Invoca aquellos elementos que cumplen exactamente con la condición que le damos (caso lógico):
mi_vector[rep(c(T, F), 10)]
##  [1] 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50

En este caso, excluye el primer valor, incluye el segundo valor, excluye el tercero, incluye el cuarto, y así sucesivamente.

  • Invoca aquellos valores de acuerdo a las operaciones lógicas que le demos:
mi_vector[(mi_vector > 28)]
##  [1] 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
rm(complex, element, Frac_Raz, 
   I_dont_Know, Je_ne_sais_pa, 
   Medicina, mi_vector, Parle_French, 
   Real_Enter, Img) 

A partir de los vectores, se pueden crear otros objetos, como los data frames, aunque también existen objetos que pueden contener datos de distinta naturaleza en un solo conjunto.

Listas

Una lista es como una caja que puede guardar múltiples objetos diferentes, todos juntos, independientemente de su tipo, tamaño y naturaleza, como números, caracteres, vectores, otros objetos como data frames, e incluso otras listas.

La función básica para crear una lista es list():

list(0, "Hola", TRUE)
## [[1]]
## [1] 0
## 
## [[2]]
## [1] "Hola"
## 
## [[3]]
## [1] TRUE

Con las siguientes funciones, podemos almacenar, nombrar y ver las características de la lista:

Lista_de_cosas <- list(T, "Crespo", 1L, 7.5, "tontos", 5+4i)

# Ver la naturaleza de cada uno de los datos de la lista:
str(Lista_de_cosas)
## List of 6
##  $ : logi TRUE
##  $ : chr "Crespo"
##  $ : int 1
##  $ : num 7.5
##  $ : chr "tontos"
##  $ : cplx 5+4i
# Nombrar los datos de cada uno de los objetos de la lista:
Num_de_Tontos <- list("Wu" = 2, "Loyd" = 2, "Garmadon" = 3, "Kay" = 4) 
View(Num_de_Tontos)

Búsqueda en listas:

La búsqueda en listas puede ser más compleja que la de vectores, debido a que el proceso se complica al determinar qué se quiere encontrar. Esta es una de las formas de hacerlo:

carro <- list(color = "rojo", 
              nllantas = 4, 
              ncilindros = 4,
              marca = "Renault") 

# Mediante la notación $ es posible la búsqueda de ciertos datos: 
carro$color 
## [1] "rojo"
# Mediante los [] es posible la búsqueda de objetos dentro de la lista: 
carro[c("ncilindros", "nllantas")] 
## $ncilindros
## [1] 4
## 
## $nllantas
## [1] 4
# A su vez, podremos buscar un dato particular mediante esta función: 
carro[["marca"]]
## [1] "Renault"

Será posible hacer búsquedas parciales mediante la opción exact = FALSE, de la siguiente forma:

carro[["col", exact = FALSE]]
## [1] "rojo"

Si tenemos una lista de listas, será mejor opción buscar con doble corchete para acceder a los datos de manera más rápida:

carro[[c(2, 1)]]
## [1] 4
rm(carro, Num_de_Tontos, Lista_de_cosas)

Matrix

Una matriz en R es una estructura de datos bidimensional que organiza los datos en filas y columnas, como una tabla. Es similar a un vector, pero con una dimensión extra (filas y columnas). Todas las entradas de una matriz deben ser del mismo tipo de dato y tener la misma naturaleza (para más información, lee el Groosman).

Para crear una matriz, se utiliza la función matrix():

Matriz <- matrix(data = 1:12,
                 nrow = 4,  # Número de filas
                 ncol = 3)  # Número de columnas  

print(Matriz) 
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12
rm(Matriz)
# Más información con esta función:
?matrix()

Igualmente, es posible crear matrices con las funciones cbind() (con la cual especificamos el número de columnas) y rbind() (especificamos el número de filas):

# Se especifica el rango de datos con el que se creará la matriz:
cbind(4:15)
##       [,1]
##  [1,]    4
##  [2,]    5
##  [3,]    6
##  [4,]    7
##  [5,]    8
##  [6,]    9
##  [7,]   10
##  [8,]   11
##  [9,]   12
## [10,]   13
## [11,]   14
## [12,]   15
# Generalmente será posible definir los datos que queremos: 
rbind(c(4, 10, 24, 36, 1, -4))
##      [,1] [,2] [,3] [,4] [,5] [,6]
## [1,]    4   10   24   36    1   -4
# Con esta función es posible crear matrices a partir de un data.frame:
Gasto_Gm <- data.matrix(Gasto_G)

Con esta misma función también es posible elegir las columnas del data frame que queremos usar:

Gasto_Gm <- data.matrix(Gasto_G[-1]) 
summary(Gasto_Gm) 
##        gm               im        
##  Min.   :  3931   Min.   :  1967  
##  1st Qu.: 25538   1st Qu.: 25826  
##  Median : 34233   Median : 47027  
##  Mean   : 45295   Mean   : 61520  
##  3rd Qu.: 51385   3rd Qu.: 78703  
##  Max.   :374627   Max.   :344837

Búsqueda en matrices:

La búsqueda en matrices es una combinación de la búsqueda en vectores y listas. En este caso, deberemos poner las coordenadas exactas para encontrar el dato que buscamos, de la siguiente manera:

Del lado izquierdo se pone la coordenada de la fila y del lado derecho la columna en la que buscaremos:

Gasto_Gm[36, 2]
##       im 
## 20203.26
# Así, por ejemplo, podremos buscar solamente las filas de esta manera: 
Gasto_Gm[45, ] 
##       gm       im 
## 11010.97 23184.76
# Y de esta forma seleccionar la columna que queremos: 
Gasto_gobierno <- Gasto_Gm[, c("gm"), drop = F]
head(Gasto_gobierno) 
##            gm
## [1,]  4652.96
## [2,] 35303.82
## [3,] 15423.22
## [4,]  4204.52
## [5,]  5730.85
## [6,]  5695.64
# Combinando las dos, invocamos un dato específico: 
Gasto_Gm[45, c("gm")] 
##       gm 
## 11010.97
# También podemos invocar un rango de datos de esta forma: 
Gasto_M <- Gasto_Gm[45:67, , drop = F]
nrow(Gasto_M)
## [1] 23
# Estos elementos pueden ser excluidos con esta función:  
Gasto_R <- Gasto_Gm[-45:-67, c("im"), drop = F]
nrow(Gasto_Gm)
## [1] 213
nrow(Gasto_R) 
## [1] 190
# Así confirmamos que se ha excluido un cierto número de variables.

# También podemos seleccionar ciertos datos específicos de esta forma:
Gasto_Gm[c(65, 66, 70, 94, 133), c("gm", "im")] 
##            gm       im
## [1,] 27734.94 30057.05
## [2,] 28603.25 30326.07
## [3,] 28484.34 31352.44
## [4,] 19627.99 44104.90
## [5,] 62079.15 58466.71
rm(Gasto_gobierno, Gasto_M, Gasto_R)

Finalmente, como podrás observar, se invocan los datos pero en un formato de vector básico. Esto puede cambiar con la función drop, que permite mantener la naturaleza de matriz. Este truco también funciona para los data frames:

# Ejemplo 1: 
Gobierno_Gm <- Gasto_Gm[, c("gm"), drop = F] 
class(Gobierno_Gm)
## [1] "matrix" "array"
# Ejemplo 2:
Gobierno_Im <- Gasto_Gm[, c("im"), drop = F] 
class(Gobierno_Im) 
## [1] "matrix" "array"
rm(Gobierno_Gm, Gobierno_Im)

Data.Frame

Un data frame en R es una estructura de datos muy utilizada que se asemeja a una tabla o una hoja de cálculo, donde los datos se organizan en filas y columnas. Cada columna en un data frame puede contener datos de distinta naturaleza (números, caracteres, factores, etc.), pero todos los valores dentro de una misma columna deben ser del mismo tipo. Además, el número de datos contenidos en cada columna debe ser igual para que el data frame funcione correctamente.

Para crear data frames, se pueden fusionar vectores con la función data.frame():

# Ejemplo 1
Kaldor <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Keynes <- c(10, 9, 8, 7, 6, 5, 4, 3, 2, 1) 

# Ejemplo 2
Fritura <- c("Takis", "Top_Tops", 
             "Sabritas_Crujientes", 
             "Doritos", "Runners")
Puntuaje <- c(2, 4, 1, 5, 3)

# Creación de los Data Frames:
Economistas <- data.frame(Kaldor, Keynes) 
Frituras <- data.frame(Fritura, Puntuaje) 

# Ver Data Frames:
print(Economistas)
##    Kaldor Keynes
## 1       1     10
## 2       2      9
## 3       3      8
## 4       4      7
## 5       5      6
## 6       6      5
## 7       7      4
## 8       8      3
## 9       9      2
## 10     10      1
print(Frituras) 
##               Fritura Puntuaje
## 1               Takis        2
## 2            Top_Tops        4
## 3 Sabritas_Crujientes        1
## 4             Doritos        5
## 5             Runners        3
rm(Kaldor, Keynes, Frituras, Puntuaje, Economistas) 

Asimismo, es posible convertir estructuras de datos de distinta naturaleza en un data frame con as.data.frame():

Gasto <- as.data.frame(Gasto_Gm)
class(Gasto)
## [1] "data.frame"
rm(Gasto, Gasto_Gm) 

Es importante mencionar que cuando importamos una base de datos con alguno de los métodos vistos previamente, estos ya se cargan directamente como un data frame, por lo que no será necesario realizar una conversión.

Búsqueda en un Data.Frame:

La forma de buscar elementos en un data frame es muy similar a la búsqueda en una matriz, pero con algunas características adicionales provenientes de las listas:

data(cars)
  • Al igual que en las listas, podemos invocar una columna usando el nombre de la misma:
cars$speed
##  [1]  4  4  7  7  8  9 10 10 10 11 11 12 12 12 12 13 13 13 13 14 14 14 14 15 15
## [26] 15 16 16 17 17 17 18 18 18 18 19 19 19 20 20 20 20 20 22 23 24 24 24 24 25
  • También heredamos la opción lógica de las listas, pero en este caso podemos agregar más condicionantes, como elegir un valor de una fila cuyo valor en otra columna cumpla una cierta condición:
cars$speed[cars$dist > 100]
## [1] 24
  • Otra opción muy útil es combinar múltiples condiciones usando operadores lógicos como & (AND) o | (OR), de la siguiente manera:
Mujeres_ID <- Inmigración$edad[Inmigración$ent == "Distrito Federal" & 
                                 Inmigración$sexo == "Mujeres"]

mean(Mujeres_ID)
## [1] 54.5
  • Finalmente, es posible usar la función setdiff() para eliminar columnas por medio de sus nombres y no por su número:
Gasto_01 <- Gasto_G[, setdiff(names(Gasto_G), c("i"))]

En caso de querer borrar más columnas, puedes hacerlo agregando los nombres en el vector de setdiff() como c(“columna1”, “columna2”).

Ejemplos de búsqueda en un Data.Frame:

Para calcular el promedio de edad de las mujeres o personas menores de 20 años que han inmigrado alguna vez en su vida, es necesario hacer una búsqueda que sirva como filtro para estudiar dicha población:

GruposV_I <- Inmigración$sexo[Inmigración$edad > 20 | 
                              Inmigración$sexo == "Mujeres"]

Con la variable GruposV_I, es posible saber cuántas personas menores de 20 años o mujeres han inmigrado al país:

table(GruposV_I) 
## GruposV_I
## Hombres Mujeres 
##  122197  151030
rm(Gasto_01, Mujeres_ID, GruposV_I, cars)

Tibbles

Los tibbles son una versión mejorada de los data frames en R, diseñados para hacer más fácil y eficiente el manejo de datos. Los tibbles provienen del paquete tibble, que es parte del conjunto de herramientas del tidyverse. Si bien los data frames tradicionales son muy útiles, los tibbles introducen ciertas mejoras que los hacen más amigables y menos propensos a errores en algunas situaciones.

Para usarlos, es necesario cargar la paquetería tibble, así como utilizar la función tibble() para crear un tibble:

library("tibble")
tb <- tibble(
  Nombre = c("Juan", "Ana", "Luis", "Marta"),
  Edad = c(23, 25, 21, 22),
  Ciudad = c("Madrid", "Barcelona", "Valencia", "Sevilla")) 

print(tb) 
## # A tibble: 4 × 3
##   Nombre  Edad Ciudad   
##   <chr>  <dbl> <chr>    
## 1 Juan      23 Madrid   
## 2 Ana       25 Barcelona
## 3 Luis      21 Valencia 
## 4 Marta     22 Sevilla
# También es posible crear los tibbles de la siguiente manera:
tibble(x = 1:5, y = 1, z = x^2 + y)
rm(tb)

Asimismo, es posible convertir un data frame a un tibble con la función as_tibble():

Inmigración_Tb <- as_tibble(Inmigración) 

Entre sus principales características respecto a los data frames tradicionales están:

Output Limitado:

A diferencia de los data frames tradicionales, los tibbles no imprimen todo el contenido de manera automática cuando los visualizas en la consola. En su lugar, muestran las primeras 10 filas y solo las columnas que caben en la pantalla, lo cual es muy útil cuando trabajas con conjuntos de datos grandes.

Dado que los datos abarcan un gran número de filas, omitiremos el proceso completo:

# Con el data.frame:
glimpse(Inmigración)
## Rows: 302,060
## Columns: 8
## $ renglon     <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ año         <int> 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990…
## $ ent         <chr> "Aguascalientes", "Aguascalientes", "Aguascalientes", "Agu…
## $ id_ent      <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ cvegeo      <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ sexo        <chr> "Hombres", "Hombres", "Hombres", "Hombres", "Hombres", "Ho…
## $ edad        <int> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, …
## $ inmigintnal <dbl> 6, 10, 13, 12, 10, 8, 8, 7, 6, 4, 3, 3, 3, 2, 2, 2, 2, 2, …
# Con el tibble:
print(Inmigración_Tb)
## # A tibble: 302,060 × 8
##    renglon   año ent            id_ent cvegeo sexo     edad inmigintnal
##      <int> <int> <chr>           <int>  <int> <chr>   <int>       <dbl>
##  1       1  1990 Aguascalientes      1      1 Hombres     0           6
##  2       2  1990 Aguascalientes      1      1 Hombres     1          10
##  3       3  1990 Aguascalientes      1      1 Hombres     2          13
##  4       4  1990 Aguascalientes      1      1 Hombres     3          12
##  5       5  1990 Aguascalientes      1      1 Hombres     4          10
##  6       6  1990 Aguascalientes      1      1 Hombres     5           8
##  7       7  1990 Aguascalientes      1      1 Hombres     6           8
##  8       8  1990 Aguascalientes      1      1 Hombres     7           7
##  9       9  1990 Aguascalientes      1      1 Hombres     8           6
## 10      10  1990 Aguascalientes      1      1 Hombres     9           4
## # ℹ 302,050 more rows

Subconjuntos Sin drop = FALSE:

Cuando seleccionas una sola columna de un tibble, el resultado sigue siendo un tibble (a diferencia de los data frames, donde a veces el resultado puede ser un vector si no usas el argumento drop = FALSE). Esto evita errores relacionados con la transformación inesperada de datos.

Inm_AyE <- Inmigración_Tb[Inmigración_Tb$año == "2023" & 
                          Inmigración_Tb$ent %in% 
                            c("Distrito Federal", "México"), 
                            c(setdiff(names(Inmigración_Tb), 
                            c("renglon", "id_ent", "cvegeo")))]
# Verificación:
class(Inm_AyE)
## [1] "tbl_df"     "tbl"        "data.frame"

Otras características de los tibbles incluyen:

  • Los strings permanecen como character por defecto.
  • Avisos de errores más claros que los data frames.
  • Capacidad para contener listas dentro de ellos.
  • Capacidad de búsqueda de datos de la forma tradicional.

Conociendo esto, podrás considerar cuál de los dos marcos de datos te conviene usar. Cada uno tiene sus ventajas, pero lo interesante de los tibbles es su manipulación interna. Por ello, veremos brevemente cómo manipular marcos de datos.

Edición del Marco de Datos

La edición de un marco de datos (data frame) en R se refiere a la manipulación, modificación o transformación de su estructura y contenido para adaptarlo a las necesidades del análisis. Esto incluye operaciones como:

Estas operaciones son fundamentales para preparar los datos antes de realizar análisis estadísticos, modelado o visualización. Para lograr dicho propósito se usa la paquetería dplyr, que viene incluida en el tidyverse. Simplemente cargando esta paquetería bastará para empezar:

library("dplyr")
library("magrittr") 
library("here") 
library("skimr")
library("janitor") 

Pipes

Las pipes (o tubos) (%>%) en R son una herramienta del paquete magrittr que nos permiten filtrar datos de un marco de datos de manera secuencial, encadenando funciones. Esto hace que el código sea más legible y fácil de escribir, especialmente cuando realizas varias operaciones consecutivas sobre un conjunto de datos.

Para invocar un tubo se utiliza la combinación Ctrl + Shift + M. Su funcionamiento permite pasar el resultado de una función directamente como argumento a la siguiente función, sin necesidad de crear variables intermedias o anidar llamadas de funciones.

Ejemplo de Pipes:

Para obtener las estadísticas básicas de la edad de las mujeres menores de 20 años que inmigraron de la Ciudad de México entre 1994 y el año 2000, en R Base generalmente sería de la siguiente manera:

# Primero generamos la tabla base para los estadísticos:
InmigraciónF_CDMX <- Inmigración[Inmigración$año >= 1994 & 
                                 Inmigración$año <= 2000 &
                                 Inmigración$sexo == "Mujeres" &
                                 Inmigración$edad < 20 & 
                                 Inmigración$ent == "Distrito Federal",         
                                 c(setdiff(names(Inmigración), 
                                 c("renglon", "id_ent", "cvegeo",
                                   "inmigintnal")))]

# Después generamos los estadísticos deseados:
Est_Descriptiva_F_01 <- c(Num_Mujeres = sum(InmigraciónF_CDMX$sexo == 
                                              "Mujeres"),
                          Media_Edad = mean(InmigraciónF_CDMX$edad), 
                          Mediana_Edad = median(InmigraciónF_CDMX$edad),
                          Desv_Est_Edad = sd(InmigraciónF_CDMX$edad))

# Finalmente, visualizamos los estadísticos:
print(Est_Descriptiva_F_01)
##   Num_Mujeres    Media_Edad  Mediana_Edad Desv_Est_Edad 
##    140.000000      9.500000      9.500000      5.786986

Como se puede observar, el proceso es complejo y anidado, lo que lo hace difícil de entender completamente. Por eso, el uso de pipes mejora este tipo de filtro.

En R Magrittr podemos obtener los estadísticos directamente sin necesidad de generar una tabla previa, conservando el formato original mediante los tibbles:

Est_Descriptiva_F_02 <- Inmigración_Tb %>% 
                        select(año, ent, sexo, edad) %>% 
                        filter(año >= 1994 & año <= 2000, 
                               sexo == "Mujeres",
                               ent == "Distrito Federal",
                               edad < 20) %>% 
                        summarise(Num_Mujeres = sum(sexo == "Mujeres"),
                                  Media_Edad = mean(edad), 
                                  Mediana_Edad = median(edad),
                                  Desv_Est_Edad = sd(edad)) 

# Posteriormente, simplemente revisamos los resultados:
print(Est_Descriptiva_F_02) 
## # A tibble: 1 × 4
##   Num_Mujeres Media_Edad Mediana_Edad Desv_Est_Edad
##         <int>      <dbl>        <dbl>         <dbl>
## 1         140        9.5          9.5          5.79
rm(InmigraciónF_CDMX, Est_Descriptiva_F_01, Est_Descriptiva_F_02)

Como puedes ver, las pipes simplifican considerablemente la tarea de filtrar una base de datos. Esto no solo nos ayuda a ser más rápidos en la tarea, sino que también permite que nuestro código sea más organizado. Por lo tanto, es importante saber utilizarlas para optimizar nuestro trabajo.

Resumen de Marcos de Datos:

La primera forma de saber qué tipo de datos tenemos es a través de un resumen general de nuestro marco de datos. Esto se puede hacer con las siguientes funciones:

# Para este caso, usaremos los datos de esta paquetería:
library("palmerpenguins")

Funciones base de R:

  • La función head() muestra las primeras filas de un data frame o tibble. Por defecto, se muestran las primeras 6 filas, pero puedes especificar un número diferente de filas.
head(penguins)
  • La función str() (de “structure”) proporciona un resumen de la estructura del dataset. Muestra información como el número de observaciones (filas), el número de variables (columnas) y el tipo de datos de cada columna (factor, numérico, carácter, etc.).
str(penguins)
## tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
##  $ species          : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ island           : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
##  $ bill_length_mm   : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
##  $ bill_depth_mm    : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
##  $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
##  $ body_mass_g      : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
##  $ sex              : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
##  $ year             : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...

Es una manera compacta de ver la estructura del data frame y los primeros valores de cada columna.

  • La función summary() da un resumen estadístico básico de cada variable en el dataset. Para variables numéricas, muestra el mínimo, el primer cuartil, la mediana, la media, el tercer cuartil y el máximo. Para factores, muestra la frecuencia de los niveles.
summary(penguins)
##       species          island    bill_length_mm  bill_depth_mm  
##  Adelie   :152   Biscoe   :168   Min.   :32.10   Min.   :13.10  
##  Chinstrap: 68   Dream    :124   1st Qu.:39.23   1st Qu.:15.60  
##  Gentoo   :124   Torgersen: 52   Median :44.45   Median :17.30  
##                                  Mean   :43.92   Mean   :17.15  
##                                  3rd Qu.:48.50   3rd Qu.:18.70  
##                                  Max.   :59.60   Max.   :21.50  
##                                  NA's   :2       NA's   :2      
##  flipper_length_mm  body_mass_g       sex           year     
##  Min.   :172.0     Min.   :2700   female:165   Min.   :2007  
##  1st Qu.:190.0     1st Qu.:3550   male  :168   1st Qu.:2007  
##  Median :197.0     Median :4050   NA's  : 11   Median :2008  
##  Mean   :200.9     Mean   :4202                Mean   :2008  
##  3rd Qu.:213.0     3rd Qu.:4750                3rd Qu.:2009  
##  Max.   :231.0     Max.   :6300                Max.   :2009  
##  NA's   :2         NA's   :2

Este resumen proporciona una visión rápida de los valores clave en cada variable y es especialmente útil para ver estadísticas básicas como la mediana, la media y la cantidad de datos faltantes (NA).

Funciones de dplyr y skimr:

  • La función glimpse() es similar a str(), pero proporciona un resumen más compacto y legible. Muestra los nombres de las variables, su tipo y los primeros valores de cada una, todo en una línea.
glimpse(penguins)
## Rows: 344
## Columns: 8
## $ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
## $ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse…
## $ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, …
## $ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, …
## $ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186…
## $ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, …
## $ sex               <fct> male, female, female, NA, female, male, female, male…
## $ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007…

Es una manera más compacta de ver la estructura del data frame y los primeros valores de cada columna.

  • La función skim_without_charts() proporciona un resumen más detallado y fácil de leer que summary(), incluyendo información sobre distribuciones, valores ausentes (NA) y el tipo de cada variable.
skim_without_charts(penguins)
Data summary
Name penguins
Number of rows 344
Number of columns 8
_______________________
Column type frequency:
factor 3
numeric 5
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
species 0 1.00 FALSE 3 Ade: 152, Gen: 124, Chi: 68
island 0 1.00 FALSE 3 Bis: 168, Dre: 124, Tor: 52
sex 11 0.97 FALSE 2 mal: 168, fem: 165

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100
bill_length_mm 2 0.99 43.92 5.46 32.1 39.23 44.45 48.5 59.6
bill_depth_mm 2 0.99 17.15 1.97 13.1 15.60 17.30 18.7 21.5
flipper_length_mm 2 0.99 200.92 14.06 172.0 190.00 197.00 213.0 231.0
body_mass_g 2 0.99 4201.75 801.95 2700.0 3550.00 4050.00 4750.0 6300.0
year 0 1.00 2008.03 0.82 2007.0 2007.00 2008.00 2009.0 2009.0

Este resumen es más detallado que summary() e incluye información sobre valores faltantes (NA) y estadísticas descriptivas detalladas de cada variable.

  • Finalmente, con la función select() es posible generar resúmenes de ciertas columnas del marco de datos para analizarlas separadamente.
summary(select(penguins, species, sex, year))
##       species        sex           year     
##  Adelie   :152   female:165   Min.   :2007  
##  Chinstrap: 68   male  :168   1st Qu.:2007  
##  Gentoo   :124   NA's  : 11   Median :2008  
##                               Mean   :2008  
##                               3rd Qu.:2009  
##                               Max.   :2009
  • O para eliminar una columna que no es de nuestro interés:
skim_without_charts(select(penguins, -species, -sex, -year))
Data summary
Name select(penguins, -species…
Number of rows 344
Number of columns 5
_______________________
Column type frequency:
factor 1
numeric 4
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
island 0 1 FALSE 3 Bis: 168, Dre: 124, Tor: 52

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100
bill_length_mm 2 0.99 43.92 5.46 32.1 39.23 44.45 48.5 59.6
bill_depth_mm 2 0.99 17.15 1.97 13.1 15.60 17.30 18.7 21.5
flipper_length_mm 2 0.99 200.92 14.06 172.0 190.00 197.00 213.0 231.0
body_mass_g 2 0.99 4201.75 801.95 2700.0 3550.00 4050.00 4750.0 6300.0

Con estas funciones, podemos empezar la inspección de nuestros datos y limpiarlos para, por ejemplo, cambiar el nombre de un cierto número de variables.

Cambio de nombres:

Para poder cambiar el nombre de las columnas de nuestro marco de datos, primero debemos saber cuáles son esos nombres, los cuales se pueden visualizar con colnames():

colnames(penguins)
## [1] "species"           "island"            "bill_length_mm"   
## [4] "bill_depth_mm"     "flipper_length_mm" "body_mass_g"      
## [7] "sex"               "year"
hotel_data <- read_csv("C:/Users/Zack_/OneDrive/R-Proyects/Archivos/hotel_bookings.csv", col_names = TRUE)

Sabiendo los nombres, podremos usar la función rename() para iniciar la transformación:

  • Cambiar el nombre de las columnas:
rename(penguins, sexo = sex)
  • Cambiar las características de los títulos de las columnas:
rename_with(penguins, toupper) # Cambia los títulos a mayúsculas.
rename_with(penguins, tolower) # Cambia los títulos a minúsculas.

Ejemplos de rename y resumen:

Gracias a los “tubos” (pipes), podemos cambiar el nombre de tres columnas del objeto hotel_data, guardarlas en un tibble único y luego generar un resumen general del mismo:

colnames(hotel_data)
##  [1] "hotel"                          "is_canceled"                   
##  [3] "lead_time"                      "arrival_date_year"             
##  [5] "arrival_date_month"             "arrival_date_week_number"      
##  [7] "arrival_date_day_of_month"      "stays_in_weekend_nights"       
##  [9] "stays_in_week_nights"           "adults"                        
## [11] "children"                       "babies"                        
## [13] "meal"                           "country"                       
## [15] "market_segment"                 "distribution_channel"          
## [17] "is_repeated_guest"              "previous_cancellations"        
## [19] "previous_bookings_not_canceled" "reserved_room_type"            
## [21] "assigned_room_type"             "booking_changes"               
## [23] "deposit_type"                   "agent"                         
## [25] "company"                        "days_in_waiting_list"          
## [27] "customer_type"                  "adr"                           
## [29] "required_car_parking_spaces"    "total_of_special_requests"     
## [31] "reservation_status"             "reservation_status_date"
trimmed_df <- hotel_data %>%
              select(hotel, is_canceled, lead_time) %>%
              rename(Hotel = hotel, 
                     Cancelados = is_canceled, 
                     Tiempo = lead_time)
summary(trimmed_df)
##     Hotel             Cancelados         Tiempo   
##  Length:119390      Min.   :0.0000   Min.   :  0  
##  Class :character   1st Qu.:0.0000   1st Qu.: 18  
##  Mode  :character   Median :0.0000   Median : 69  
##                     Mean   :0.3704   Mean   :104  
##                     3rd Qu.:1.0000   3rd Qu.:160  
##                     Max.   :1.0000   Max.   :737

Realizaremos algo similar con la función Michel_K, cambiando el nombre de las variables de nuestro interés. Posteriormente, aplicaremos el logaritmo natural a todas las variables para, finalmente, generar un resumen de los datos:

colnames(Michel_K)
##  [1] "Trimestre"   "INPC = 2018" "PIB"         "P = EBO"     "I = FBC"    
##  [6] "CG"          "Im"          "DF"          "X"           "M"          
## [11] "BC"          "AB"          "IK"          "s"           "A"
Ganancias <- Michel_K %>%
  rename(G = "P = EBO", 
         I = "I = FBC", 
         T = Im) %>%
  select(-c("INPC = 2018", AB, Trimestre, DF, BC)) %>%
  log() %>%
  skim_without_charts()

View(Ganancias)

rm(Ganancias, trimmed_df)

Filtrado de datos:

El filtrado de datos es fundamental para poder empezar a transformar nuestros datos y continuar con un análisis más detallado de estos. Esto se puede hacer de diversas formas, pero siempre teniendo en cuenta los pipes como un factor clave para que este proceso sea lo más sencillo posible.

Un primer ejemplo es mediante la función arrange(), que simplemente ordena los datos de mayor a menor, como en el siguiente caso:

# En este caso, poniendo el símbolo - se invierte el orden.
año <- arrange(penguins, -year)

La función filter(), vista con anterioridad, nos permite seleccionar un cierto tipo de datos con características específicas, como en el siguiente caso:

islands <- penguins %>% filter(island == "Torgersen") %>% 
                         filter(bill_length_mm <= 58.0)

head(islands)

También es posible simplificar el filtro, como se realizó en la búsqueda de elementos del data frame:

islands <- penguins %>% filter(island == "Torgersen" & 
                                bill_length_mm <= 58.0)
head(islands)
rm(islands, año)

Otra función muy útil es group_by(), que a diferencia de select, agrupa las filas según una o más columnas para hacer cálculos. Esta función se enfoca en filas y grupos, permitiendo un filtrado más específico que el anterior:

Sexo <- penguins %>% 
  group_by(sex) %>% 
  drop_na() %>% 
  summarise(Sex_Mean_Length = mean(bill_length_mm), 
            Sex_Max_Length = max(bill_length_mm)) 
print(Sexo)
## # A tibble: 2 × 3
##   sex    Sex_Mean_Length Sex_Max_Length
##   <fct>            <dbl>          <dbl>
## 1 female            42.1           58  
## 2 male              45.9           59.6

Generalmente, group_by() se usa para crear resúmenes estadísticos a nuestro modo, algo que sería más complicado con la función select():

sex_estadistic <- penguins %>% 
  group_by(species, island, sex) %>% 
  drop_na() %>% 
  summarize(Mean_Flipper = mean(flipper_length_mm), 
            Desv_Flipper = sd(flipper_length_mm),
            .groups = "drop") %>% 
  arrange(species)

#Media y Desviación Estandar de las aletas:
print(sex_estadistic)
## # A tibble: 10 × 5
##    species   island    sex    Mean_Flipper Desv_Flipper
##    <fct>     <fct>     <fct>         <dbl>        <dbl>
##  1 Adelie    Biscoe    female         187.         6.74
##  2 Adelie    Biscoe    male           190.         6.46
##  3 Adelie    Dream     female         188.         5.51
##  4 Adelie    Dream     male           192.         6.80
##  5 Adelie    Torgersen female         188.         4.64
##  6 Adelie    Torgersen male           195.         5.92
##  7 Chinstrap Dream     female         192.         5.75
##  8 Chinstrap Dream     male           200.         5.98
##  9 Gentoo    Biscoe    female         213.         3.90
## 10 Gentoo    Biscoe    male           222.         5.67
rm(Sexo, sex_estadistic)

Creación, Unión y Separación:

Ya con nuestro marco de datos creado, será posible realizar la combinación, fusión y separación de nuevas columnas, utilizando diversas funciones que veremos a continuación.

Mediante la función mutate(), podremos agregar una nueva columna combinando dos o más columnas o simplemente aplicando un operador a estas, con el objetivo de generar una nueva variable que sea de interés.

Ejemplos de creación de variables:

Crearemos un tibble llamado Noches_Promedio, que nos indique el porcentaje de las noches respecto a las noches promedio que generalmente suelen tomar los huéspedes por mes. Para crear este estadístico, necesitaremos la función mutate(), así como otras funciones vistas previamente para generar una nueva columna.

# Generación del Tibble:
Noches_Promedio <- hotel_data %>% 
  rename(Mes = arrival_date_month) %>% 
  filter(is_canceled == 1) %>% 
  mutate(Noches = stays_in_week_nights / mean(stays_in_week_nights)) %>% 
  select(Noches, Mes)

View(Noches_Promedio)

Como podemos observar, las noches que superan el valor de 1 son aquellas que sobrepasan el promedio de noches que los huéspedes suelen permanecer en el hotel. Ahora podremos ver cuáles son los meses que logran superar dicha característica.

# Generación de la gráfica: 
ggplot(Noches_Promedio, aes(x = Mes, y = Noches)) + 
  geom_bar(stat = "summary", 
           fun = "mean",
           fill = "goldenrod",
           color = "black", 
           width = 0.8) +  
  geom_hline(yintercept = 1, 
             color = "black", 
             linetype = "dashed", 
             size = 1) +
  labs(title = "Noches en el hotel por mes",
       subtitle = "En términos porcentuales",
       x = "Meses del año",  
       y = "Noches reservadas") + 
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, 
                              size = 20, 
                              face = "bold"),
    plot.subtitle = element_text(hjust = 0.5, 
                                 size = 12, 
                                 face = "italic", 
                                 color = "#2F2F2F"),
    axis.title.x = element_text(size = 13),
    axis.title.y = element_text(size = 13), 
    panel.grid.major = element_line(color = "#2F2F2F", 
                                    size = 0.5), 
    panel.background = element_rect(fill = "#D3D3D3"), 
    plot.background = element_rect(fill = "pink"))

Como se puede observar, solamente los meses de marzo, junio, julio y agosto superan el valor de 1, lo cual nos indica que en esos meses en particular, la duración de estancias por noche en el hotel suele ser mayor al promedio general.

Otro ejemplo que realizaremos es la generación de una gráfica de densidad acerca de los residuos de la regresión de las ganancias respecto a otros estadísticos, con el fin de analizar si nuestros residuos presentan un comportamiento acorde a la media y tienden a tener una distribución normal:

  1. Primero debemos crear un Autoregresivo, lo cual puede hacerse de diversas formas. La más básica es simplemente generar los errores y guardarlos en un objeto:
# Creación del autorregresivo: 
G_Logaritmo <- tibble(G = log(Michel_K$`P = EBO`))
Arima <- ar(G_Logaritmo, order.max = 1) 
Arima_1 <- tibble(Residuos = as.numeric(Arima$resid))
head(Arima_1) 
rm(G_Logaritmo, Arima)
  1. Posteriormente, crearemos el objeto que contendrá nuestros datos. En este caso, lo llamaremos Modelo_Kaleckiano, y contendrá la información necesaria:
# Creación del tibble:
Modelo_Kaleckiano <- Michel_K %>% 
                     rename(Ganancias_G = 'P = EBO',
                            Inversion_I = 'I = FBC', 
                            Consumo_Gobierno_CG = 'CG', 
                            Impuestos_T = 'Im', 
                            Defic_Fiscal_DF = 'DF',
                            Exportaciones_X = 'X',
                            Importaciones_M = 'M', 
                            Balanza_Comercial_BC = 'BC', 
                            Ingreso_Capitalistas_Ik = 'IK', 
                            Ahorro_s = 's') %>%  
                     mutate(Conjunto_A = Inversion_I + 
                                       Defic_Fiscal_DF + 
                                       Balanza_Comercial_BC, 
                             Constante_A = lag(Conjunto_A, 1), 
                             Ahorro_st = lag(Ahorro_s, 1)) %>% 
                     select(Ganancias_G, 
                            Ahorro_st,
                            Constante_A, 
                            Inversion_I) %>%
                     log() %>%  
                     mutate(Autoregresivo_G = Arima_1$Residuos)

rm(Arima_1, Michel_K)
head(Modelo_Kaleckiano)
  1. Ya con el objeto generado, será posible crear la regresión con la función lm(), obtener el intercepto y los estadísticos relacionados a ella, y verificar que pase todas las pruebas generales de un modelo de regresión lineal:
# Creación de la Regresión lineal:
Regresión_G <- lm(Ganancias_G ~ Inversion_I + Constante_A + Ahorro_st + Autoregresivo_G, data = Modelo_Kaleckiano)
summary(Regresión_G)
## 
## Call:
## lm(formula = Ganancias_G ~ Inversion_I + Constante_A + Ahorro_st + 
##     Autoregresivo_G, data = Modelo_Kaleckiano)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.060304 -0.020430 -0.002984  0.020759  0.073826 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      8.87307    0.72200  12.290  < 2e-16 ***
## Inversion_I      0.22646    0.04934   4.589 2.55e-05 ***
## Constante_A      0.12665    0.02641   4.796 1.24e-05 ***
## Ahorro_st        0.12674    0.01539   8.234 3.19e-11 ***
## Autoregresivo_G  0.69859    0.09760   7.158 1.90e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.02884 on 56 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.8476, Adjusted R-squared:  0.8367 
## F-statistic: 77.85 on 4 and 56 DF,  p-value: < 2.2e-16
  1. Verificando que las pruebas generales se cumplan, podemos comprobar si los residuos de la regresión tienden a la normalidad. Este es solo un análisis gráfico; para ejecutar correctamente la prueba deberás realizar la prueba Jarque-Bera:
Density_G <- density(Regresión_G$residuals)
par(bg = "pink")
plot(Density_G, 
     main = "Densidad del Modelo Kaleckiano", 
     cex.main = 2, 
     font.main = 2, 
     ylab = "Densidad",
     col = "black", 
     lwd = 1, 
     type = "l") 
mtext("Prueba de normalidad", 
      side = 3, 
      line = 0.3, 
      cex = 1.0)
polygon(Density_G, 
        col = "black", 
        border = "black")

Como se observa en la gráfica, los residuos parecen comportarse de manera normal. Sin embargo, es necesario realizar una prueba de normalidad convencional para confirmarlo.

rm(Density_G, Modelo_Kaleckiano, Regresión_G)

Finalmente, será posible unir distintas columnas mediante la función unite(), para fusionarlas y crear una sola, como se muestra a continuación:

example_unite <- hotel_data %>%
  select(arrival_date_year, 
         arrival_date_month) %>% 
  unite(Mes_y_Año, 
        c("arrival_date_month", "arrival_date_year"), 
        sep = " ") 
head(example_unite)

Con la función separate(), podemos separar las columnas de manera sencilla:

example_separate <- example_unite %>%
  separate(Mes_y_Año, 
           into = c("Mes", "Año"), 
           sep = " ") 
head(example_separate)

De esta forma, podemos modificar gran parte de nuestros marcos de datos a nuestro antojo. Estas funciones son muy útiles. A continuación, veremos algunas funciones más avanzadas en R, que llevarán este proceso al siguiente nivel:

Próximamente…

rm(example_unite, example_separate)