2 Procesamiento de Base de Datos
El presente capítulo tiene como objetivo registrar y explicar diversos códigos de utilidad para el procesamiento y limpieza de bases de datos. De esta manera, el capítulo se subdivide en diversas estrategias con las cuales el analista podrá enfrentarse a las bases de datos antes de comenzar con los procedimientos estadísticos.
Dentro de cada subdivisión se encontrarán una serie de formas para realizar el mismo procedimiento, cubriendo así posibles errores y maximizando la utilidad de esta guía.
2.1 Operadores de utilidad general
2.1.1 1. Operadores relacionales
Se usan para hacer comparaciones. Cuando en la Tabla 1 nos referimos a un valor, esto refiere tambien a variables
| Símbolo | Función |
|---|---|
< |
Un valor es menor que otro |
> |
Un valor es mayor que otro |
== |
Un valor es igual que otro [^1] |
<= |
Un valor es menor o igual que otro |
>= |
Un valor es mayor o igual que otro |
!= |
Un valor es distinto o diferente que otro |
%in% |
Un valor pertenece al conjunto designado [^2] |
is.na() |
El valor es perdido o NA |
!is.na() |
El valor es distinto de NA |
2.1.2 2. Operadores aritméticos
Realizan operaciones, como la suma, resta, división, entre otros.
| Símbolo | Función |
|---|---|
+ |
Suma |
- |
Resta |
* |
Multiplicación |
/ |
División |
^ |
Elevado |
2.1.3 3. Operadores de asignación
Hay dos formas de asignar objetoA <- objetoB o objetoA = objetoB. Ambas
implican que lo que se este realizando en el objetoB implica que eso va a
producir o generar al objetoA
2.1.4 4. Operadores booleanos
Describen relaciones lógicas o condicionales
Estos operadores responden a la jerarquía según paréntesis
| Símbolo | Función |
|---|---|
& |
Indica un y lógico |
| |
Indica un o lógico |
xor() |
Excluye la condición |
! |
Distinto de … |
any |
Ninguna de las condiciones serán utilizadas |
all |
Todas las condiciones serán ocupadas |
2.1.5 5. Operador pipeline (%>%)
¡Aquí mucha atención! Este operador %>% (llamado pipe) no es un operador que este contenido en las funciones base del lenguaje R. Este operador proviene de la función magrittr de tidyverse, y es de los operadores más útiles y utilizados en R.
¿Para qué sirve? Para concatenar múltiples funciones y procesos. Imagina que quieres filtrar una base de datos a partir de tramos etarios. Pero no tienes esa variable creada. ¿Que hacer? La respuesta: concatenar el proceso de creación de variables y luego filtrar. Eso se puede hacer gracias a %>% (ya mostraremos como utilizar esta herramienta), que por lo demás es muy fácil de ejecutar.
Ctrl + shift + MPara Windows⌘ + shift + MPara Mac
2.2 Manipulación de estructutura de bases de datos
2.2.1 Tipos de variables
Antes de hacer cualquier cálculo con un nuevo conjunto de datos, es importante asegurarnos que nuestro dataframe se encuentre en óptimas condiciones y nuestras variables posean las características necesarias para realizar los análisis solicitados.
Recordemos que en R trabajamos con los siguientes tipos de variables
| Tipo de dato | Descripción | Ejemplo |
|---|---|---|
| integer | Números enteros | -1, 0, 1 |
| numeric | Números reales | -0.5, 1/2, 1 |
| character | Texto | “palabra” “y” |
| Factor | Categorías | 1 = Mujer; 2 = Hombre |
| logical | Verdadero o falso | “TRUE”, “FALSE” |
En relación a lo anterior, un problema que solemos observar al trabajar con R y RStudio es, por ejemplo, que nuestras variables numéricas estén codificadas como character, es decir, R considera esos valores como texto. Para solucionar este tipo de problemas utilizamos la “coerción explícita”, es decir, cambiamos la naturaleza de la variable a nuestra discreción.
Para ello debemos, en primer lugar, consultar la estructura de datos. De esta forma evaluamos si el conjunto de variables, o una variable específica, requiere la aplicación de una coerción explícita. Existen diversas maneras de realizar este procedimiento, a través de R base o mediante librerías.
1. R Base
# Para este ejemplo cargamos BBDD en el objeto df
df <- irisConsulta de estructura de datos para todo el dataframe
str(df)## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
Como podemos observar, este código nos entrega un detallado informe sobre los tipos de datos que cuenta nuestro dataframe, indicando el nombre, su naturaleza y los valores que considera
Consulta de estructura de dato para una variable específica
# a) utilizamos la función str(), pero indicamos la variable de interés
str(df$Petal.Length)## num [1:150] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
# b) la función class() nos arroja la naturaleza de lo que solicitamos.
# Para este caso queremos saber la clase de la variable "Species"
# alojada en nuestro dataframe
class(df$Species)## [1] "factor"
Tal como expresan los resultados, la función str() se comporta de la misma manera que cuando se aplicó a todo el dataframe. En cambio la función class() solo nos entrega la naturaleza de la variable, sin otro tipo de información. En tal sentido, la variable “Species” es de tipo factor.
2. Librería Janitor
Si bien la función str() funciona de manera correcta y nos entrega información precisa, muchas veces tal información no es de nuestra utilidad, pues solo buscamos saber la naturaleza de la variable, y ello sumado a que como analistas generalmente trabajamos con enormes BBDD, precisamos de alguna herramienta que únicamente nos entregue la información de interés. Para este caso existen otras funciones que se albergan en librerías de terceros, siendo Janitor una de ellas.
# Cargamos la librería
library(janitor)# Utilizamos la función compare_df_cols(). Esto nos entrega de manera
# simplificada los tipos de datos de nuestro dataframe
compare_df_cols(df)## column_name df
## 1 Sepal.Length numeric
## 2 Sepal.Width numeric
## 3 Petal.Length numeric
## 4 Petal.Width numeric
## 5 Species factor
2.2.2 Coerción explícita
Una vez identificamos las variables que no cumplen con nuestros requerimientos, procedemos a intervenirlas mediante coerción explícita.
- Transformar a variable numérica
# evaluamos nuestras variables
compare_df_cols(df)## column_name df
## 1 Sepal.Length character
## 2 Sepal.Width numeric
## 3 Petal.Length numeric
## 4 Petal.Width numeric
## 5 Species factor
Como observamos, la variable “Sepal.Length” es tipo character, no obstante, si obervamos nuestro dataframe, la variable debería ser de tipo numérica. Para corregir esta inconsistencia debemos usar la función as.numeric() y luego corroboramos que el cambio se haya realizado:
# coerción explícita
df$Sepal.Length <- as.numeric(df$Sepal.Length)
# comprobación
class(df$Sepal.Length)## [1] "numeric"
- Transformar a variable character
# evaluamos nuestras variables
compare_df_cols(df)## column_name df
## 1 Sepal.Length numeric
## 2 Sepal.Width numeric
## 3 Petal.Length numeric
## 4 Petal.Width numeric
## 5 Species numeric
En este caso, la variable “Species” es de tipo numérica, aun cuando se trata de respuestas de tipo texto. Para corregir la variable y transofrmarla a tipo texto, utilizamos la función as.character()
# coerción explícita
df$Species<- as.character(df$Species)
# comprobación
class(df$Species)## [1] "character"
- Transformar a variable factor
En el paso anterior hemos convertido la variable a character, pero ¿qué ocurre si en el libro de códigos las categorías de esta variable se encuentran ligadas a un orden específico? Para resolver este tipo de situaciones utilizamos la función as.factor() y comprobamos su naturaleza
# coerción explícita
df$Species <- as.factor(df$Species)
# comprobación
class(df$Species)## [1] "factor"
Ok, de acuerdo a nuestra evaluación hemos convertido la variable a tipo factor, ¿pero qué ocurre si le pedimos a R una tabla de frecuencias simple?
table(df$Species)##
## 1 2 3
## 50 50 50
Tal como indica la tabla, la variable contiene únicamente los niveles en términos numéricos, no se encuentran asignadas a las categorías que representan. Si lo que queremos es convertir a factor y además indicar los nombres de las categorías, debemos utilizar la función factor(), pues esta función nos permite integrar otros argumentos de utilidad para completar la coerción.
# Coerción. Para este caso convertimos la variable Species a factor
# y además definimos las categorías con el argumento "labels = "
df$Species <- factor(df$Species, labels = c("setosa",
"versicolor",
"virginica"))
# compbrobación
class(df$Species)## [1] "factor"
# frecuencias simple
table(df$Species)##
## setosa versicolor virginica
## 50 50 50
De acuerdo a los resultados, hemos coercionado la variable a tipo factor y, además de ello, según lo expuesto la tabla de frecuencias simple, logramos asignar los niveles o categorías a la variable.
- Transformar a variable integer
Por último, vamos a crear una nueva variable numérica que utilizaremos como identificador de la especie. Para este caso, al tratarse de un identificador, queremos establecer que contenga únicamente números enteros, debido a esto, haremos una coerción a variable “integer”.
Primero creamos nuestra nueva variable que acturará como identificador de la especie.
# utilizaremos las funciones presentes en tidyverse para crear nuestra nueva variable
library(tidyverse)
# construcción de nueva variable
df <- df %>%
mutate(Categoria = case_when(Species == "setosa" ~ 1,
Species == "versicolor" ~ 2,
Species == "virginica" ~ 3))Ahora que tenemos nuestra variable, vamos a conertirla a una variable integer mediante coerción explícita utilizando la función “as.integer”.
# Coerción. Para este caso convertimos la nueva variable a integer
df$Categoria <- as.integer(df$Categoria)
# compbrobación
class(df$Categoria)## [1] "integer"
2.3 Crear nuevas variables
Ahora que aprendimos los tipos de datos y cómo coercionarlos, podemos proceder a crear nuestras propias variables. Para ello, de igual manera que al consultar la estructura de datos, podemos utilizar R base o apoyarnos en librerías de terceros.
2.3.1 1. R Base
# Para este ejemplo utilizaremos la base de datos mtcars que será almacenada en el objeto "df"
df <- mtcarsComo primer ejercicio, crearemos la variable peso_kg, la cual corresponderá los pesos de los vehículos en kilogramos, ya que en la base original estos pesos vienen en libras.
Para realizar este procedimiento debemos asignar el nombre de variable, seguido de la operación que queremos implementar en la variable. Para este caso, la transformación de libras a kilos, la cual se realiza multiplicando el peso el libras por mil, para luego dividirlo en dos.
# creación de nueva variable
df$peso_kg <- (df$wt*1000)/2
# observamos nuestra nueva variable
df %>%
select(wt, peso_kg) %>%
head() %>%
flextable()wt | peso_kg |
2.620 | 1,310.0 |
2.875 | 1,437.5 |
2.320 | 1,160.0 |
3.215 | 1,607.5 |
3.440 | 1,720.0 |
3.460 | 1,730.0 |
Como podemos apreciar, se ha creado la variable peso_kg de manera correcta.
2.3.2 2. Tidyverse
Si bien R Base nos permite crear variables e integrarlas a nuestro conjunto de datos, existen librerías que nos proporcionan mayores opciones para crear nuestras variables. Este es el caso de “tidyverse”, donde podemos definir variables utilizando formatos condicionales, o incluso replicar la construcción de la variable peso_kg mediante la función “mutate”.
# creación de peso_kg con sintaxis tidyverse
df <- df %>%
mutate(peso_kg = (wt*1000)/2)
# observamos nuestra nueva variable
df %>%
select(wt, peso_kg) %>%
head() %>%
flextable()wt | peso_kg |
2.620 | 1,310.0 |
2.875 | 1,437.5 |
2.320 | 1,160.0 |
3.215 | 1,607.5 |
3.440 | 1,720.0 |
3.460 | 1,730.0 |
Continuando con el ejercicio anterior, y haciendo uso de esta librería, haremos una nueva variable que nos indique, según nuestros parámetros, cuando un auto es pesado, liviano o ni pesado ni liviano.
Utilizaremos la función “case_when” para indicar nuestras condiciones.
# creamos nuestra variable peso_text
df <- df %>%
mutate(peso_text = case_when(peso_kg > 1800 ~ "Pesado",
peso_kg < 1000 ~ "Liviano",
peso_kg > 1000 & peso_kg < 1800 ~ "Ni pesado, ni livano"))
# observamos nuestras nuevas variables
df %>%
select(peso_kg, peso_text) %>%
head() %>%
flextable(cwidth = 2)peso_kg | peso_text |
1,310.0 | Ni pesado, ni livano |
1,437.5 | Ni pesado, ni livano |
1,160.0 | Ni pesado, ni livano |
1,607.5 | Ni pesado, ni livano |
1,720.0 | Ni pesado, ni livano |
1,730.0 | Ni pesado, ni livano |
Al seleccionar nuestras nuevas variables podemos corroborar que tanto peso_kg como peso_text fueron creadas y almacenadas con éxito en nuestro conjunto de datos.
Otra manera de hacer esto es utilizando valores lógicos y la función “ifelse”, es decir, valores “TRUE” o “FALSE” si se cumple la condición. En este sentido, se crea una variable que indica “TRUE” cuando la condición se cumple.
df <- df %>%
mutate(auto_pesado = ifelse(peso_kg > 1800, TRUE, FALSE)) %>%
mutate(auto_liviano = ifelse(peso_kg < 1000, TRUE, FALSE)) %>%
mutate(auto_nini = ifelse(peso_kg > 1000 & peso_kg < 18000, TRUE, FALSE))
df %>%
select(peso_kg, auto_pesado, auto_liviano, auto_nini) %>%
head() %>%
flextable()peso_kg | auto_pesado | auto_liviano | auto_nini |
1,310.0 | FALSE | FALSE | TRUE |
1,437.5 | FALSE | FALSE | TRUE |
1,160.0 | FALSE | FALSE | TRUE |
1,607.5 | FALSE | FALSE | TRUE |
1,720.0 | FALSE | FALSE | TRUE |
1,730.0 | FALSE | FALSE | TRUE |
También podemos crear variables dummy, asignando el valor 1 cuando se cumpla la condición y 0 cuando no.
# variable dummy cuando el auto es pesado
df %>%
mutate(pesado_dummy = ifelse(peso_kg > 1800, 1, 0)) %>%
arrange(desc(pesado_dummy)) %>%
select(peso_kg, pesado_dummy) %>%
head() %>%
flextable()peso_kg | pesado_dummy |
2,035.0 | 1 |
1,865.0 | 1 |
1,890.0 | 1 |
2,625.0 | 1 |
2,712.0 | 1 |
2,672.5 | 1 |
De esta manera, pesado_dummy nos indica con el valor 1 todos aquellos autos que pesen más de 1800 kg.
2.4 Limpiar y renombrar variables
Muchas veces las variables que integran las bases de datos poseen nombres que no son lo bastante intuitivos. Nos encontramos con variables que tienen nombres con mayúsculas, espacios u otros caracteres especiales, o simplemente no aluden a la lógica de la variable. Para estos casos podemos limpiar los nombres mediante librerías o renombrar las variables de forma manual.
2.4.1 1. Limpiar nombre de variables, librería “janitor”
Para esta sección utilizaremos una base de datos que contiene información sobre países del mundo.
# librería que contiene los datos
library(gapminder)
# asignamos los datos al objeto df
df <- gapminder# primero observamos los nombres de nuestro conjunto de datos y evaluamos si se debe aplicar,
# o no, una limpieza de nombres. Para esta caso, contamos con variables que integran letras
# mayúsculas, las cuales pueden entorpecer la manera de generar código.
head(df) %>%
flextable()country | continent | Year | lifeExp | pop | gdpPercap |
Afghanistan | Asia | 1,952 | 28.801 | 8,425,333 | 779.4453 |
Afghanistan | Asia | 1,957 | 30.332 | 9,240,934 | 820.8530 |
Afghanistan | Asia | 1,962 | 31.997 | 10,267,083 | 853.1007 |
Afghanistan | Asia | 1,967 | 34.020 | 11,537,966 | 836.1971 |
Afghanistan | Asia | 1,972 | 36.088 | 13,079,460 | 739.9811 |
Afghanistan | Asia | 1,977 | 38.438 | 14,880,372 | 786.1134 |
# también podemos solicitar solo los nombres de las variables
names(df)## [1] "country" "continent" "Year" "lifeExp"
## [5] "pop" "gdpPercap"
# para limpiar los espacios, mayúsculas o caracteres especiales de nuestras variables utilizaremos
# nuevamente la librería Janitor
library(janitor)
# limpiar los nombres de mi conjunto de datos
df <- clean_names(df)
# observamos los cambios
head(df) %>% # vemos las primeras seis observaciones del df
flextable()country | continent | year | life_exp | pop | gdp_percap |
Afghanistan | Asia | 1,952 | 28.801 | 8,425,333 | 779.4453 |
Afghanistan | Asia | 1,957 | 30.332 | 9,240,934 | 820.8530 |
Afghanistan | Asia | 1,962 | 31.997 | 10,267,083 | 853.1007 |
Afghanistan | Asia | 1,967 | 34.020 | 11,537,966 | 836.1971 |
Afghanistan | Asia | 1,972 | 36.088 | 13,079,460 | 739.9811 |
Afghanistan | Asia | 1,977 | 38.438 | 14,880,372 | 786.1134 |
names(df) # vemos solo nos nombres de las variables## [1] "country" "continent" "year"
## [4] "life_exp" "pop" "gdp_percap"
Como podemos ver, las variables “life_exp”, “year” y “gdp_percap” transformaron sus letras a minúscula de manera automática.
2.4.2 2. Renombrar variables
Si no estamos contentos con el trabajo automático que realiza janitor podemos renombrar las variables de forma manual. Para ello tenemos funciones de R base y de otras librerías.
2.4.2.0.1 - R base
Con R base podemos renombrar variables según su posición en la base de datos. En este caso la primera variable es “country”, pero cambiaremos su nombre a “pais”.
# renombrar variable utilizando la posición que utiliza en la base de datos
# colnames(data)[posición] <- "nuevo_nombre"
colnames(df)[1] <- "pais"
# corroboramos el cambio de nombre de la primera variable
names(df)## [1] "pais" "continent" "year"
## [4] "life_exp" "pop" "gdp_percap"
También podemos renombrar variables en conjunto. Por ejemplo, renombrar desde la variable 2 a la 4
# renombrar variables
colnames(df)[2:4] <- c("continente",
"año",
"esperanza_vida")
# comprobar los cambios
names(df)## [1] "pais" "continente"
## [3] "año" "esperanza_vida"
## [5] "pop" "gdp_percap"
2.4.2.1 - Tidyverse
Además de R base, también contamos con una función especial dentro del Tidyverse: rename(), la cual cuenta con diversas formas de renombrar.
Forma 1: rename() manera por defecto de utilizar la función.
# rename(nombre_nuevo = nombre_original) + corroborar nombres
df %>%
rename(esperanza_vida = life_exp,
poblacion = pop) %>%
names()## [1] "country" "continent"
## [3] "year" "esperanza_vida"
## [5] "poblacion" "gdp_percap"
Forma 2: rename_if() nos permite reemplazar los nombres de las variables siguiendo alguna condición que indiquemos.
# renombrar todas las variables numéricas
df %>%
rename_if(is.numeric, ~ c("año", "esperanza_vida", "poblacion", "ingreso_percap")) %>%
names()## [1] "country" "continent"
## [3] "año" "esperanza_vida"
## [5] "poblacion" "ingreso_percap"
Como se observa, se reemplazaron todas las variables numéricas.
2.5 Reordenar variables
Una vez que hemos creado o renombrado nuestras variables, seguramente querremos organizarlas en un orden determinado.
Retomando el conjunto de datos mtcars y las nuevas variables que fuimos incorporando, nos quedó una base de datos como esta:
mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | peso_kg | peso_text |
21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | 1,310.0 | Ni pesado, ni livano |
21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | 1,437.5 | Ni pesado, ni livano |
22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | 1,160.0 | Ni pesado, ni livano |
21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | 1,607.5 | Ni pesado, ni livano |
18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | 1,720.0 | Ni pesado, ni livano |
18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | 1,730.0 | Ni pesado, ni livano |
Tal como se muestra en la tabla, nuestras nuevas variables fueron creadas al final del conjunto de datos. Mas, no queremos mantener este orden, sino que esperamos tener la variable peso_kg y peso_text junto a la variable wt, para así mantener un orden en nuestro dataframe. Esto se logra realizar de diversas maneras, tanto desde R base como de librerías y funciones personalizadas que nos simplifican el proceso.
2.5.1 1. R base
Utilizando ubicación de las variables
Recordemos que buscábamos ordenar wt, peso_kg, peso_text, y si observamos la tabla, lo hemos conseguido solo utilizando las posiciones de las variables.
Para este caso, en el código mantuvimos las posiciones de las variables 1:6, correspondientes desde mpg a wt, para luego indicar que las variables con posiciones 12 y 13 (peso_kg y peso_text respectivamente) seguirían a la variable 6 (wt), y finalmente mantener el orden de de las variables que ocupan la posición 7 a la 11.
# reordenar variables usando sus posiciones
df[, c(1:6,12,13, 7:11)] %>%
head() %>%
flextable()mpg | cyl | disp | hp | drat | wt | peso_kg | peso_text | qsec | vs | am | gear | carb |
21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 1,310.0 | Ni pesado, ni livano | 16.46 | 0 | 1 | 4 | 4 |
21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 1,437.5 | Ni pesado, ni livano | 17.02 | 0 | 1 | 4 | 4 |
22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 1,160.0 | Ni pesado, ni livano | 18.61 | 1 | 1 | 4 | 1 |
21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 1,607.5 | Ni pesado, ni livano | 19.44 | 1 | 0 | 3 | 1 |
18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 1,720.0 | Ni pesado, ni livano | 17.02 | 0 | 0 | 3 | 2 |
18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 1,730.0 | Ni pesado, ni livano | 20.22 | 1 | 0 | 3 | 1 |
2.5.2 2. Tidyverse
a) Función relocate()
En el ejemplo le hemos indicado a R que las variables “peso_kg” y “peso_text” se posicionen después de la variables “wt”.
# utilizando los nombres de las variables
# relocate(c("var_x"), after = "variable_a_la_que_seguiran")
df %>%
relocate(c("peso_kg", "peso_text"), .after = "wt") %>%
head() %>%
flextable()mpg | cyl | disp | hp | drat | wt | peso_kg | peso_text | qsec | vs | am | gear | carb |
21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 1,310.0 | Ni pesado, ni livano | 16.46 | 0 | 1 | 4 | 4 |
21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 1,437.5 | Ni pesado, ni livano | 17.02 | 0 | 1 | 4 | 4 |
22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 1,160.0 | Ni pesado, ni livano | 18.61 | 1 | 1 | 4 | 1 |
21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 1,607.5 | Ni pesado, ni livano | 19.44 | 1 | 0 | 3 | 1 |
18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 1,720.0 | Ni pesado, ni livano | 17.02 | 0 | 0 | 3 | 2 |
18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 1,730.0 | Ni pesado, ni livano | 20.22 | 1 | 0 | 3 | 1 |
# de igual manera, con el argumento "before =" se puede indicar las variables
# se posicionen antes de una variable específica
# (en caso obtendríamos la misma tabla)
# df %>% relocate(c("peso_kg", "peso_text"), .before = "qsec")También podemos realizar este mismo procedimiento utilizando la posición que ocupan las variables en nuestro conjunto de datos.
En este caso hemos indicado que las variables 12 y 13 (peso_kg y peso_texto) se posicionen antes que la variable 7 (qsec).
# utilizando la posición de las variables
# relocate(c("var_x"), before = "variable")
df %>%
relocate(12:13, .before = 7) %>%
head() %>%
flextable()mpg | cyl | disp | hp | drat | wt | peso_kg | peso_text | qsec | vs | am | gear | carb |
21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 1,310.0 | Ni pesado, ni livano | 16.46 | 0 | 1 | 4 | 4 |
21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 1,437.5 | Ni pesado, ni livano | 17.02 | 0 | 1 | 4 | 4 |
22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 1,160.0 | Ni pesado, ni livano | 18.61 | 1 | 1 | 4 | 1 |
21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 1,607.5 | Ni pesado, ni livano | 19.44 | 1 | 0 | 3 | 1 |
18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 1,720.0 | Ni pesado, ni livano | 17.02 | 0 | 0 | 3 | 2 |
18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 1,730.0 | Ni pesado, ni livano | 20.22 | 1 | 0 | 3 | 1 |
b) Función select()
Otra de las funciones presentes el tidyverse es select(), de la librería dplyr. Existen diversas manera de utilizar esta función, sin embargo, la eficiencia de estas dependerán de la cantidad de variables que contenga nuestra base de datos.
b.1) Cuando contamos con pocas variables
La primera manera de utilizar esta función es seleccionando el orden de las variables de acuerdo a los nombres que poseen en el dataframe.
Para este ejemplo utilizaremos la siguiente tabla y buscaremos cambiar el orden de las variables departamento y salario, donde esta última ocupe la segunda posición de la tabla.
id | departmento | salario |
7,058 | IT | 34,500.00 |
7,059 | sales | 560,890.78 |
7,060 | finance | 67,000.78 |
7,089 | IT | 25,000.00 |
7,072 | finance | 78,900.00 |
7,078 | sales | 25,000.00 |
Para realizar este cambio utilizamos la función select(), indicando el nombre de las variables según el orden que buscamos. Para este caso, la variable id queremos que se encuentre en la primera posición, seguido de salario y departamento, dando paso a la siguiente tabla.
# reordenar con select() utilizando los nombres de las variables
data %>%
select(id, salario, departmento) %>%
head() %>%
flextable()id | salario | departmento |
7,058 | 34,500.00 | IT |
7,059 | 560,890.78 | sales |
7,060 | 67,000.78 | finance |
7,089 | 25,000.00 | IT |
7,072 | 78,900.00 | finance |
7,078 | 25,000.00 | sales |
b.2) Cuando trabajamos con muchas variables
Generalmente como analistas nos enfrentamos a manejar bases de datos con múltiples variables. Para aquellos casos, la función anterior no sería la más eficiente, pues deberíamos escribir un sin fin de variables según sus nombres, los cuales muchas veces no son de los más sencillos.
Para estos casos es mejor utilizar las posiciones de las variables. Un ejemplo: tenemos una gran base de datos con diez variables que corresponden a una tanda de preguntas relacionadas y necesitamos llevarlas al inicio de nuestro conjunto de datos, ¿cómo lo hacemos?
Para este ejemplo tenemos la siguiente base datos, con 19 variables, de las cuales las últimas 10 las queremos posicionar después de la variable “day”.
# librería que contiene la BBDD
library(nycflights13)
# cargar BBDD al objeto df
df <- flights
# observamos nuestros datos
df %>%
head() %>%
datatable(fillContainer = TRUE)Como habíamos mencionado, en estos casos es más eficiente trabajar con las posiciones numéricas de las variables. En este sentido, en el código hemos indicado que mantendremos las posiciones de las primeras tres variables, seguidas de las variables 10 a la 19, y finalizando con las variables de la 4 a la 9, dando paso a la siguiente distribución de la base de datos:
# ordenar variables con select() utilizando las posiciones de las variables
df %>%
select(1:3, 10:19, 4:9) %>%
head() %>%
datatable(fillContainer = TRUE)2.5.3 3. Función personalizada
Si bien las anteriores funciones nos permiten lograr nuestros cometidos de reordenar las variables a nuestro antojo, estas lo hacen en largas líneas de código o nos hacen interactuar con aquellas otras variables que no queremos modificar.
Debido a lo anterior, se ha creado la siguiente función personalizada que nos permite reordenar las variables de una manera mucho más sencilla, donde solo debemos asignar la posición que queremos que utilice la variable.
Para que la función se integre a nuestro entorno de trabajo debemos copiar el código a continuación y correrlo en nuestro script o chunk de .rmd. De esta manera, la función se guardará en nuestro enviroment bajo el nombre de “arrange.vars”, permitiéndonos llamarla y utilizarla cuando deseemos.
Código con la función: copiar y pegar en nuestro script
## FORMA DE UTILIZAR LA FUNCION ##
## df <- arrange.vars(c("nombre.variable.x"= posición))
## df <- arrange.vars(c("nombre.variable.A"= 1,
## "nombre.variable.B" = 12,
## "nombre.variable.C = X))
arrange.vars <- function(data, vars){
##stop if not a data.frame (but should work for matrices as well)
stopifnot(is.data.frame(data))
##sort out inputs
data.nms <- names(data)
var.nr <- length(data.nms)
var.nms <- names(vars)
var.pos <- vars
##sanity checks
stopifnot( !any(duplicated(var.nms)),
!any(duplicated(var.pos)) )
stopifnot( is.character(var.nms),
is.numeric(var.pos) )
stopifnot( all(var.nms %in% data.nms) )
stopifnot( all(var.pos > 0),
all(var.pos <= var.nr) )
##prepare output
out.vec <- character(var.nr)
out.vec[var.pos] <- var.nms
out.vec[-var.pos] <- data.nms[ !(data.nms %in% var.nms) ]
stopifnot( length(out.vec)==var.nr )
##re-arrange vars by position
data <- data[ , out.vec]
return(data)
}Ahora que se ha creado la función en nuestro enviroment podremos llamarla y trabajar con ella. Retomando nuestro ejemplo anterior, obtuvimos la siguiente tabla, pero me he percatado que la variable “time_hour” estaría mejor posicionada si estuviese seguida a la variable “day”.
Para reordenar únicamente la variable “time_hour” utilzaremos la nueva función creada, indicando que queremos que ocupe la posición 4 en nuestro dataframe. Como se observa, el cambio fue efectivo.
Utilizar esta función personalizada agiliza el código y nos permite trabajar tanto con variables individuales como conjuntos.
df %>%
arrange.vars(c("time_hour" = 4)) %>%
head() %>%
datatable(fillContainer = TRUE)2.6 Recodificación de variables
Una vez que hemos creado, limpiado, renombrado y reordenado nuestras variables, es probable que debamos realizar ciertas recodificaciones a estas variables. Nuevamente, para ello contamos con una alternativa de R base, o también mediante librerías de terceros.
Para los siguientes ejercicios utilizaremos una BBDD que contiene información de autos. En ella recodificaremos la variable cyl que se encuentra en valores numéricos a las catergoría de “baja cilindrada”, “media cilindrada” y “alta cilindrada”
# cargar BBDD al objeto df
df <- mpgmanufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
audi | a4 | 1.8 | 1,999 | 4 | auto(l5) | f | 18 | 29 | p | compact |
audi | a4 | 1.8 | 1,999 | 4 | manual(m5) | f | 21 | 29 | p | compact |
audi | a4 | 2.0 | 2,008 | 4 | manual(m6) | f | 20 | 31 | p | compact |
audi | a4 | 2.0 | 2,008 | 4 | auto(av) | f | 21 | 30 | p | compact |
audi | a4 | 2.8 | 1,999 | 6 | auto(l5) | f | 16 | 26 | p | compact |
audi | a4 | 2.8 | 1,999 | 6 | manual(m5) | f | 18 | 26 | p | compact |
2.6.1 1. R base
Una manera de recodificar variables utilizando R base es mediante la aplicación de los corchetes “[]”.
a) Recodificar en la misma variable
Como se puede apreciar en la tabla, en la variable cyl, aquellos valores que eran “4” ahora son “baja cilindrada”, los que eran “6” son ahora “media cilindrada”, y aquellos de valor “8” ahora son “alta cilindrada”
# recodificar con R base en la misma variable
df$cyl[df$cyl == 4] <- "baja cilindrada"
df$cyl[df$cyl == 6] <- "media cilindrada"
df$cyl[df$cyl == 8] <- "alta cilindrada"
# BBDD con recodificación
df %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
audi | a4 | 1.8 | 1,999 | baja cilindrada | auto(l5) | f | 18 | 29 | p | compact |
audi | a4 | 1.8 | 1,999 | baja cilindrada | manual(m5) | f | 21 | 29 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | manual(m6) | f | 20 | 31 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | auto(av) | f | 21 | 30 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | auto(l5) | f | 16 | 26 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | manual(m5) | f | 18 | 26 | p | compact |
b) Recodificar en distintas variables
Anteriormente recodificamos dentro de la misma variable, pero habrán momentos en que necesitemos crear una nueva variable con esta recodificación para así no alterar la variable original.
Esto también lo podemos realizar empleando los corchetes, pues únicamente debemos cambiar la variable de destino por el nombre que llevará la nueva variable. En nuestro caso, la variable que contenga la recodificación será “cyl_rec”.
Como se puede apreciar en la tabla, aquellos valores “4” de la varible cyl se guardaron en cyl_rec como “baja cilindrara”, aquellos 6 como “media cilindrada” y los “8” como “alta cilindrada”.
# recodificar con R base en distinta variable
df$cyl_rec[df$cyl == 4] <- "baja cilindrada"
df$cyl_rec[df$cyl == 6] <- "media cilindrada"
df$cyl_rec[df$cyl == 8] <- "alta cilindrada"
# BBDD con recodificación
df %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class | cyl_rec |
audi | a4 | 1.8 | 1,999 | 4 | auto(l5) | f | 18 | 29 | p | compact | baja cilindrada |
audi | a4 | 1.8 | 1,999 | 4 | manual(m5) | f | 21 | 29 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | 4 | manual(m6) | f | 20 | 31 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | 4 | auto(av) | f | 21 | 30 | p | compact | baja cilindrada |
audi | a4 | 2.8 | 1,999 | 6 | auto(l5) | f | 16 | 26 | p | compact | media cilindrada |
audi | a4 | 2.8 | 1,999 | 6 | manual(m5) | f | 18 | 26 | p | compact | media cilindrada |
2.6.2 2. Tidyverse
Otra herramienta útil que posee el universo tidy es la función recode(). Ella nos permitirá recodificar diversas variables dentro de un mismo código.
Retomemos el ejercicio anterior con esta nueva sintaxys de recodificar.
a) Recodificar en la misma variable
# 1ra forma: sin usar condicionales
df %>%
mutate(cyl = dplyr::recode(cyl, "4" = "baja cilindrada", "6" = "media cilindrada", "8" = "alta cilindrada")) %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
audi | a4 | 1.8 | 1,999 | baja cilindrada | auto(l5) | f | 18 | 29 | p | compact |
audi | a4 | 1.8 | 1,999 | baja cilindrada | manual(m5) | f | 21 | 29 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | manual(m6) | f | 20 | 31 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | auto(av) | f | 21 | 30 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | auto(l5) | f | 16 | 26 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | manual(m5) | f | 18 | 26 | p | compact |
# 2da forma: utilizando condicionales (en este caso "case_when")
df %>%
mutate(cyl = case_when(cyl == 4 ~ "baja cilindrada",
cyl == 6 ~ "media cilindrada",
cyl == 8 ~ "alta clindrada")) %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
audi | a4 | 1.8 | 1,999 | baja cilindrada | auto(l5) | f | 18 | 29 | p | compact |
audi | a4 | 1.8 | 1,999 | baja cilindrada | manual(m5) | f | 21 | 29 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | manual(m6) | f | 20 | 31 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | auto(av) | f | 21 | 30 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | auto(l5) | f | 16 | 26 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | manual(m5) | f | 18 | 26 | p | compact |
b) Recodificar en distintas variables
Tal como aprendimos en el apartado de crear variables, para hacer esto el tidyverse utilizamos la función mutate() y asignamos el nombre de la nueva variable. De esta manera, nuestra recodificación se ha guardado en la nueva variable “cyl_rec”.
# 1ra forma: sin usar condicionales
df %>%
mutate(cyl_rec = dplyr::recode(cyl, "4" = "baja cilindrada", "6" = "media cilindrada","8" = "alta cilindrada")) %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class | cyl_rec |
audi | a4 | 1.8 | 1,999 | 4 | auto(l5) | f | 18 | 29 | p | compact | baja cilindrada |
audi | a4 | 1.8 | 1,999 | 4 | manual(m5) | f | 21 | 29 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | 4 | manual(m6) | f | 20 | 31 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | 4 | auto(av) | f | 21 | 30 | p | compact | baja cilindrada |
audi | a4 | 2.8 | 1,999 | 6 | auto(l5) | f | 16 | 26 | p | compact | media cilindrada |
audi | a4 | 2.8 | 1,999 | 6 | manual(m5) | f | 18 | 26 | p | compact | media cilindrada |
# 2da forma: sin usar condicionales
df %>%
mutate(cyl_rec = case_when(cyl == 4 ~ "baja cilindrada",
cyl == 6 ~ "media cilindrada",
cyl == 8 ~ "alta clindrada")) %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class | cyl_rec |
audi | a4 | 1.8 | 1,999 | 4 | auto(l5) | f | 18 | 29 | p | compact | baja cilindrada |
audi | a4 | 1.8 | 1,999 | 4 | manual(m5) | f | 21 | 29 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | 4 | manual(m6) | f | 20 | 31 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | 4 | auto(av) | f | 21 | 30 | p | compact | baja cilindrada |
audi | a4 | 2.8 | 1,999 | 6 | auto(l5) | f | 16 | 26 | p | compact | media cilindrada |
audi | a4 | 2.8 | 1,999 | 6 | manual(m5) | f | 18 | 26 | p | compact | media cilindrada |
2.6.3 3. Car
Si luego de las dos opciones anteriores aun no podemos llevar a cabo nuestra recodificación, contamos también con la librería car para realizar los cambios que buscamos. No obstante, su sintaxys es diferente a las anteriores trabajadas.
a) Recodificar en la misma variable
# recodificar con car en la misma variable
df$cyl <- car::recode(df$cyl, c('"4" = "baja cilindrada";
"6" = "media cilindrada";
"8" = "alta cilindrada"'))
# observar los cambios
df %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
audi | a4 | 1.8 | 1,999 | baja cilindrada | auto(l5) | f | 18 | 29 | p | compact |
audi | a4 | 1.8 | 1,999 | baja cilindrada | manual(m5) | f | 21 | 29 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | manual(m6) | f | 20 | 31 | p | compact |
audi | a4 | 2.0 | 2,008 | baja cilindrada | auto(av) | f | 21 | 30 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | auto(l5) | f | 16 | 26 | p | compact |
audi | a4 | 2.8 | 1,999 | media cilindrada | manual(m5) | f | 18 | 26 | p | compact |
b) Recodificar en distintas variables
# recodificar con car en distintas variables
df$cyl_rec <- car::recode(df$cyl, c('"4" = "baja cilindrada";
"6" = "media cilindrada";
"8" = "alta cilindrada"'))
# observar los cambios
df %>%
head() %>%
flextable()manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class | cyl_rec |
audi | a4 | 1.8 | 1,999 | baja cilindrada | auto(l5) | f | 18 | 29 | p | compact | baja cilindrada |
audi | a4 | 1.8 | 1,999 | baja cilindrada | manual(m5) | f | 21 | 29 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | baja cilindrada | manual(m6) | f | 20 | 31 | p | compact | baja cilindrada |
audi | a4 | 2.0 | 2,008 | baja cilindrada | auto(av) | f | 21 | 30 | p | compact | baja cilindrada |
audi | a4 | 2.8 | 1,999 | media cilindrada | auto(l5) | f | 16 | 26 | p | compact | media cilindrada |
audi | a4 | 2.8 | 1,999 | media cilindrada | manual(m5) | f | 18 | 26 | p | compact | media cilindrada |
2.7 Selección de variables
A pesar de que como analistas trabajamos con múltiples variables, existirán casos donde solo necesitaremos trabajar con ciertas variables específicas y no con todo el data set.
2.7.1 R Base
Para seleccionar variables con R base utilizamos los corchetes [], donde se trabaja de la siguiente manera:
df[filas, columnas]
Si se omiten valores en alguno de los argumentos, R asume que se busca seleccionar todos esos valores. Por ejemplo, df[,4:7], aquí seleccionamos todas las filas y las columnas de la 4 a la 7.
Procederemos a ejemplificar
# seleccionar solo las primeras 4 variables
df[,1:4] %>%
head() %>%
flextable()carat | cut | color | clarity |
0.23 | Ideal | E | SI2 |
0.21 | Premium | E | SI1 |
0.23 | Good | E | VS1 |
0.29 | Premium | I | VS2 |
0.31 | Good | J | SI2 |
0.24 | Very Good | J | VVS2 |
# seleccionar las primeras 3 variables, más la número 8 a la 10 y las primeras diez observaciones (filas)
df[1:10, c(1:3, 8:10)] %>%
flextable()carat | cut | color | x | y | z |
0.23 | Ideal | E | 3.95 | 3.98 | 2.43 |
0.21 | Premium | E | 3.89 | 3.84 | 2.31 |
0.23 | Good | E | 4.05 | 4.07 | 2.31 |
0.29 | Premium | I | 4.20 | 4.23 | 2.63 |
0.31 | Good | J | 4.34 | 4.35 | 2.75 |
0.24 | Very Good | J | 3.94 | 3.96 | 2.48 |
0.24 | Very Good | I | 3.95 | 3.98 | 2.47 |
0.26 | Very Good | H | 4.07 | 4.11 | 2.53 |
0.22 | Fair | E | 3.87 | 3.78 | 2.49 |
0.23 | Very Good | H | 4.00 | 4.05 | 2.39 |
2.7.2 Tidyverse
Nuevamente podemos utilizar las opciones que se encuentran en tidyverse para seleccionar variables. Esta opción nos permite agilizar nuestros códigos al contener funciones para filtrar de mejor manera aquellas variables que buscamos seleccionar.
# seleccionar por nombre de variable
df %>%
select(carat, cut, color) %>%
head() %>%
flextable()carat | cut | color |
0.23 | Ideal | E |
0.21 | Premium | E |
0.23 | Good | E |
0.29 | Premium | I |
0.31 | Good | J |
0.24 | Very Good | J |
# seleccionar todas las variables, menos las indicadas (-)
df %>%
select(-price) %>% # todas las variables, menos precio
head() %>%
flextable()carat | cut | color | clarity | depth | table | x | y | z |
0.23 | Ideal | E | SI2 | 61.5 | 55 | 3.95 | 3.98 | 2.43 |
0.21 | Premium | E | SI1 | 59.8 | 61 | 3.89 | 3.84 | 2.31 |
0.23 | Good | E | VS1 | 56.9 | 65 | 4.05 | 4.07 | 2.31 |
0.29 | Premium | I | VS2 | 62.4 | 58 | 4.20 | 4.23 | 2.63 |
0.31 | Good | J | SI2 | 63.3 | 58 | 4.34 | 4.35 | 2.75 |
0.24 | Very Good | J | VVS2 | 62.8 | 57 | 3.94 | 3.96 | 2.48 |
# seleccionar variables que comiencen de una manera en particular
df %>%
select(starts_with("c")) %>% #todas la variables que comiencen con la letra "c"
head() %>%
flextable()carat | cut | color | clarity |
0.23 | Ideal | E | SI2 |
0.21 | Premium | E | SI1 |
0.23 | Good | E | VS1 |
0.29 | Premium | I | VS2 |
0.31 | Good | J | SI2 |
0.24 | Very Good | J | VVS2 |
# seleccionar variables que terminen de una manera en particular
df %>%
select(ends_with("e")) %>% #todas la variables que terminen con la letra "e"
head() %>%
flextable()table | price |
55 | 326 |
61 | 326 |
65 | 327 |
58 | 334 |
58 | 335 |
57 | 336 |
2.8 Filtrar casos
2.8.1 Tidyverse
Así como necesitaremos seleccionar variables, también habrán ocasiones donde debamos filtrar nuestro conjunto de datos
# seleccion de casos donde el corte sea premium
df %>%
filter(cut == "Premium") %>%
head() %>%
flextable()carat | cut | color | clarity | depth | table | price | x | y | z |
0.21 | Premium | E | SI1 | 59.8 | 61 | 326 | 3.89 | 3.84 | 2.31 |
0.29 | Premium | I | VS2 | 62.4 | 58 | 334 | 4.20 | 4.23 | 2.63 |
0.22 | Premium | F | SI1 | 60.4 | 61 | 342 | 3.88 | 3.84 | 2.33 |
0.20 | Premium | E | SI2 | 60.2 | 62 | 345 | 3.79 | 3.75 | 2.27 |
0.32 | Premium | E | I1 | 60.9 | 58 | 345 | 4.38 | 4.42 | 2.68 |
0.24 | Premium | I | VS1 | 62.5 | 57 | 355 | 3.97 | 3.94 | 2.47 |
# seleccion de casos donde el corte del mineral sea premium, color E,
# y el precio sea mayor a 18100
df %>%
filter(cut == "Premium",
color == "E",
price > 18100) %>%
flextable()carat | cut | color | clarity | depth | table | price | x | y | z |
2.02 | Premium | E | SI2 | 61.0 | 59 | 18,117 | 8.18 | 8.12 | 4.97 |
2.12 | Premium | E | SI2 | 58.3 | 59 | 18,120 | 8.48 | 8.41 | 4.92 |
2.19 | Premium | E | SI2 | 62.4 | 61 | 18,232 | 8.31 | 8.23 | 5.16 |
2.14 | Premium | E | SI2 | 61.5 | 58 | 18,291 | 8.31 | 8.24 | 5.09 |
2.03 | Premium | E | SI2 | 61.5 | 59 | 18,310 | 8.16 | 8.24 | 5.04 |
1.70 | Premium | E | VS2 | 58.6 | 62 | 18,342 | 7.92 | 7.84 | 4.62 |
2.00 | Premium | E | SI2 | 61.8 | 56 | 18,426 | 8.12 | 8.05 | 5.00 |
2.03 | Premium | E | SI2 | 61.5 | 59 | 18,477 | 8.24 | 8.16 | 5.04 |
2.9 Bases de datos Long y Wide
¿Qué es long y qué es wider?
Existen dos formas de distribuir las bases de datos: en formato largo y ancho. El formato largo consiste en transponer columnas a filas, es decir, convertir las variables a observaciones. Mientras que el formato ancho consiste en transformar las observaciones en varables independientes.
2.9.1 1. De long a wider
Si observamos nuestros datos, podemos apreciar que la variable species se encuentra en formato long, pero necesitamos que cada especie se encuentre en variables diferenciadas, es decir, una variable independiente para cada una de las especies setosa, versicolor y virginica.
df %>%
datatable(fillContainer = TRUE)Para realizar esto utilizamos la función pivot_wider(), pertenenciente a las herramientas del tidyverse.
# transformar las los valores de Species a variables independientes
df %>%
pivot_wider(names_from = Species, # variable de donde salen los nombres
values_from = Species) %>% # valores que toman las nuevas variables
datatable(fillContainer = TRUE)Como vemos, se crearon tres nuevas variables con los nombre de cada especie, respetando los valores que contenía la variable original.
2.9.2 2. De wider a long
También podemos pasar de un formato wider a uno longer, es decir, transformar columnas a filas. Para ello utilizamos la función pivot_longer() del paquete dplyr, alojado en tidyverse.
Como vemos en la tabla, contamos con 10 variables que muestran los ingresos de las personas encuestadas (incluyendo el no sabe/no responde), las cuales queremos convertir a formato long.
# observamos nuestros datos
df %>%
head() %>%
flextable()religion | <$10k | $10-20k | $20-30k | $30-40k | $40-50k | $50-75k | $75-100k | $100-150k | >150k | Don't know/refused |
Agnostic | 27 | 34 | 60 | 81 | 76 | 137 | 122 | 109 | 84 | 96 |
Atheist | 12 | 27 | 37 | 52 | 35 | 70 | 73 | 59 | 74 | 76 |
Buddhist | 27 | 21 | 30 | 34 | 33 | 58 | 62 | 39 | 53 | 54 |
Catholic | 418 | 617 | 732 | 670 | 638 | 1,116 | 949 | 792 | 633 | 1,489 |
Don’t know/refused | 15 | 14 | 15 | 11 | 10 | 35 | 21 | 17 | 18 | 116 |
Evangelical Prot | 575 | 869 | 1,064 | 982 | 881 | 1,486 | 949 | 723 | 414 | 1,529 |
# convertir a longer
df %>%
pivot_longer(2:11, # variables a convertir
names_to = "ingreso", # variable donde se ingresarán los nombres de las variables originales
values_to = "frecuencia") %>% # variable que contendrá los valores de las variables originales
datatable(fillContainer = TRUE)2.10 Colapsar variables
En algunos casos tendremos baterías de preguntas que tendrán los valores en columnas independientes. Un ejemplo de ello es la tabla a continuación, donde cada estado civil corresponde a una variable en particular, teniendo valores en aquellas que responde la persona y valores NA en aquellas preguntas ajenas a las que respondió.
Si bien este tipo de variables son útiles, muchas veces querremos tener una nueva variable que contemple todas las respuestas de tales baterías. Para esto utilizamos la función coalese(), la cual nos permitirá colapsar todas las variables en una.
id | p1 | p2 | p3 |
153 | casado | ||
154 | soltero | ||
155 | soltero | ||
156 | viudo | ||
157 | casado |
Para el ejemplo, queremos crear una nueva variable que contenga las respuestas del estado civil. Cuando tengamos una gran batería de preguntas es mejor trabajar con un vector que contenga los nombres de las variables según su posición en el dataframe.
En este caso tenemos tres variables, las cuales se posicionan desde la columna 2 a la 4 dentro del dataframe. Para no escribir variable por variable en la función vamos a crear un vector que contenga los nombres de estas variables.
### CASOS CON 3 O MÁS VARIABLES ###
# a) creamos un vector que contenga los nombres de nuestras variables
# en este caso las variables p1, p2, p3
# en otros casos con mas preguntas solo debemos asignar el rango dentro de la funcion
# variables <- colnames(df[x:y])
variables <- colnames(df[2:4])
# b) para ejecutar correctamente el codigo debemos insertar el vector que hemos creado
# anteriormente, dentro de la funcion syms(VECTOR) y se uiliza de la siguiente manera:
# df <- df %>%
# mutate(nombre_var_nueva = coalesce(!!! syms(variables)))
df <- df %>%
mutate(estado_civil = coalesce(!!! syms(variables)))id | p1 | p2 | p3 | estado_civil |
153 | casado | casado | ||
154 | soltero | soltero | ||
155 | soltero | soltero | ||
156 | viudo | viudo | ||
157 | casado | casado |
Otra manera de hacer esto es sin utilizar el vector de variables. Este caso es recomendado cuando tenemos una cantidad menor de variables.
Para este ejemplo solo utilizaremos dos variables, lo cual no requiere mucho tiempo al escribirlas al mano y tendremos un resultado similar al anterior, pero con un cambio en el código, pues para este caso no necesitaremos los argumentos !!! syms(variables)
### CASOS CON 2 VARIABLES ###
df <- df %>%
mutate(estado_civil = coalesce(p1,p2))id | p1 | p2 | estado_civil |
153 | casado | casado | |
154 | soltero | soltero | |
155 | soltero | soltero | |
156 | casado | casado | |
157 | casado | casado |
2.11 For loops
También llamados iteración o bucles, son herramientas útiles para repetir una misma operación una serie de veces en un data.frame, vector u otros objetos. Esa cualidad permite ahorrar una cantidad importante de código, sobre todo cuando nuestro objetivo es realizar un proceso idéntico varias veces con distintos valores.
# Ejemplo 1
for (i in 1:4) {
print(i)
}## [1] 1
## [1] 2
## [1] 3
## [1] 4
La primera parte del “for”, consigna los elementos de la secuencia por la cual se irá iterando. En el caso anterior “i” asumiría por ejemplo el valor de 1, luego 2, más tarde 3, y por último 4. La secuencia no necesariamente debe ser ascendente, también puede ir disminuyendo (ejemplo, 5:1 daría como resultado 5,4,3,2,1).
Los elementos del rango no se limitan tampoco a una secuencia de números, también es posible incluir los componentes de un vector determinado. Veamos el siguiente ejemplo sobre esto último
# Ejemplo 2
rango<-c("hogar", "bus", "trabajo")
for (i in rango) {
print(i)
}## [1] "hogar"
## [1] "bus"
## [1] "trabajo"
Los loops permiten realizar una misma operación varias veces. Digamos, por ejemplo, que deseamos saber que valores al cuadrado, de los números del 10 al 13:
# Ejemplo 3
for (i in 10:13) {
print(i^2)
}## [1] 100
## [1] 121
## [1] 144
## [1] 169
O calcular el resultado de una misma operación a una cierta secuencia de números
# Ejemplo 4
x=10
for (i in 10:13) {
y=x-i
print(y)
}## [1] 0
## [1] -1
## [1] -2
## [1] -3
Algunos errores comunes al elaborar un “for” ocurren cuando no se utilizan debidamente los paréntesis que demanda el bucle. En el rango de la secuencia debe usarse “( )”, mientras que la función o cuerpo del proceso que deseamos realizar, debe ir entre llaves “{}”. Otro error común sucede al especificar el rango: si ingresamos un solo número, este no será interpretado como una secuencia, pues ese valor representa el punto de inicio, en cambio no se cuenta con uno de llegada.
Puesto que con frecuencia desearemos guardar los resultados del loop en algún tipo de objeto para usarlo fuera del “bucle”, es útil aprender como se hace. Con anterioridad, vimos que el “for” nos muestra o imprime un cierto resultado, cuando añadimos en él, el commando “Print”, pero eso no crea un objeto que pueda ser manipulado fuera del loop.
Para guardar los resultados en un vector podemos hacerlo de la siguiente forma:
# primero creamos un vector vacío
resultados<-c()
# luego montamos nuestro loop
x=10
for (i in 10:13) {
y=x-i
# Y guardamos los resultados en el vector así:
resultados<-append(resultados, y)
}resultados## [1] 0 -1 -2 -3
Para realizar este procedimiento con un data frame, una opción sería seguir un procedimiento similar al anterior:
# primero creamos un dataframe vacío
# Es importante que su longitud sea
# idéntica al número de resultados
# que guardaremos en él
df_r<-data.frame(1:4)
contador =0
# luego montamos nuestro loop
x=10
for (i in 10:13) {
y=x-i
contador <- contador + 1
# Y guardamos los resultados en el vector así:
df_r$resultados[contador]<-y
}
df_r## X1.4 resultados
## 1 1 0
## 2 2 -1
## 3 3 -2
## 4 4 -3