6.7 Joins y Set Operations
Anteriormente ya se había mencionado la unión entre matrices, por renglón o columna y de la función base::merge()
, las cuales tienen su optimización por parte de dplyr.
Por parte de las funciones que unen registros o variables, se tienen las funciones dplyr::bind_rows()
y bind_cols()
. Las mismas reglas que cbind()
y rbind()
se deben respetar.
set.seed(20)
tibble(One_Two = sample(1:2, size = 5, replace = T),
x <-Three_Four= sample(3:4, size = 5, replace = T),
Five_Six = sample(5:6, size = 5, replace = T))
tibble(One_Two = rep(2, 3), Three_Four = rep(4, 3), Five_Six = rep(5, 3))
y <-bind_rows(x,y)
# A tibble: 8 x 3
One_Two Three_Four Five_Six
<dbl> <dbl> <dbl>
1 2 3 5
2 1 4 5
3 1 3 5
4 2 4 5
5 2 4 5
6 2 4 5
7 2 4 5
8 2 4 5
tibble(One_Eigth = 1:8)
z <-bind_cols(bind_rows(x,y), z)
# A tibble: 8 x 4
One_Two Three_Four Five_Six One_Eigth
<dbl> <dbl> <dbl> <int>
1 2 3 5 1
2 1 4 5 2
3 1 3 5 3
4 2 4 5 4
5 2 4 5 5
6 2 4 5 6
7 2 4 5 7
8 2 4 5 8
Respecto a las operaciones de conjuntos se tienen las funciones dplyr::interset()
, dplyr::union
y dplyr::setdiff()
.
intersect(x, y)
# A tibble: 1 x 3
One_Two Three_Four Five_Six
<dbl> <dbl> <dbl>
1 2 4 5
union(x, y)
# A tibble: 4 x 3
One_Two Three_Four Five_Six
<dbl> <dbl> <dbl>
1 2 3 5
2 1 4 5
3 1 3 5
4 2 4 5
setdiff(x, y)
# A tibble: 3 x 3
One_Two Three_Four Five_Six
<dbl> <dbl> <dbl>
1 2 3 5
2 1 4 5
3 1 3 5
Las operaciones tipo Join son muy importantes en la extracción de información en bases de datos, dplyr
por su cuenta tiene varias funciones para trabajar.
dplyr::left_join(a, b, by = x)
: Une las bases de datos de acuerdo a la variable x tomando la preferencia sobre a.dplyr::rigth_join(a, b, by = x)
: Une las bases de datos de acuerdo a la variable x tomando la preferencia sobre b.dplyr::inner_join(a, b, by = x)
: Une las bases de datos de acuerdo a la variable x considerando a y b.dplyr::full_join(a, b, by = x)
: Une las bases de datos de acuerdo a la variable x sin ninguna preferencia, por lo que contiene todos los datos posibles.
Para ver más claro esto, véase los siguientes ejemplos.
set.seed(20)
tibble(L = sample(LETTERS[1:5], size = 5, replace = T),
a <-N = sample(1:100, size = 5, replace = T))
tibble(L = sample(LETTERS[1:5], size = 5, replace = T),
b <-Lo = sample(c(T, F), size = 5, replace = T))
a
# A tibble: 5 x 2
L N
<chr> <int>
1 C 94
2 B 62
3 A 45
4 B 41
5 E 67
b
# A tibble: 5 x 2
L Lo
<chr> <lgl>
1 E TRUE
2 A TRUE
3 E TRUE
4 B TRUE
5 D FALSE
left_join(a, b, by = "L")
# A tibble: 6 x 3
L N Lo
<chr> <int> <lgl>
1 C 94 NA
2 B 62 TRUE
3 A 45 TRUE
4 B 41 TRUE
5 E 67 TRUE
6 E 67 TRUE
right_join(a, b, by = "L")
# A tibble: 6 x 3
L N Lo
<chr> <int> <lgl>
1 B 62 TRUE
2 A 45 TRUE
3 B 41 TRUE
4 E 67 TRUE
5 E 67 TRUE
6 D NA FALSE
inner_join(a, b, by = "L")
# A tibble: 5 x 3
L N Lo
<chr> <int> <lgl>
1 B 62 TRUE
2 A 45 TRUE
3 B 41 TRUE
4 E 67 TRUE
5 E 67 TRUE
full_join(a, b, by = "L")
# A tibble: 7 x 3
L N Lo
<chr> <int> <lgl>
1 C 94 NA
2 B 62 TRUE
3 A 45 TRUE
4 B 41 TRUE
5 E 67 TRUE
6 E 67 TRUE
7 D NA FALSE
Y un ejemplo práctico: Tomando la base de datos de la población mexicana, se puede obtener los datos para los hombres y mujeres y modificarlos para que tengan un estructura similar a Population_FEntity
.
function(data){
normalization <-colnames(data) <- c("Entidad Federativa", "Población Total", unique(Population_FEntity$Rango_edad))
data %>%
data <- gather(key = "Rango_edad",
value = "Porcentaje", -c("Entidad Federativa", "Población Total")) %>%
mutate(Porcentaje = Porcentaje/100) %>%
mutate("Población" = Porcentaje * `Población Total`) %>%
select(-Porcentaje, -`Población Total`)
}
readxl::read_xls("Data/Population_Sex_FEntity.xls", range = "B43:Q74", col_names = F)
Population_FEntity_M <- normalization(Population_FEntity_M)) (Population_FEntity_M <-
# A tibble: 448 x 3
`Entidad Federativa` Rango_edad Población
<chr> <chr> <dbl>
1 Aguascalientes 0 a 4 51587.
2 Baja California 0 a 4 124995.
3 Baja California Sur 0 a 4 22289.
4 Campeche 0 a 4 37749.
5 Coahuila 0 a 4 104304.
6 Colima 0 a 4 34496.
7 Chiapas 0 a 4 170085.
8 Chihuahua 0 a 4 122434.
9 Distrito Federal 0 a 4 462096.
10 Durango 0 a 4 76977.
# … with 438 more rows
readxl::read_xls("Data/Population_Sex_FEntity.xls", range = "B79:Q110", col_names = F)
Population_FEntity_F <- normalization(Population_FEntity_F)) (Population_FEntity_F <-
# A tibble: 448 x 3
`Entidad Federativa` Rango_edad Población
<chr> <chr> <dbl>
1 Aguascalientes 0 a 4 56336
2 Baja California 0 a 4 127107.
3 Baja California Sur 0 a 4 22569
4 Campeche 0 a 4 32170
5 Coahuila 0 a 4 115765
6 Colima 0 a 4 23751.
7 Chiapas 0 a 4 227851
8 Chihuahua 0 a 4 122055
9 Distrito Federal 0 a 4 366480
10 Durango 0 a 4 68470
# … with 438 more rows
Así es fácil crear un sólo conjunto de datos donde se reúna toda la información relevante
Population_FEntity %>% select(-`Población Total`)
Population_FEntity <- left_join(Population_FEntity, Population_FEntity_M, by = c("Entidad Federativa", "Rango_edad"))
Population_FEntityWS <- left_join(Population_FEntityWS, Population_FEntity_F, by = c("Entidad Federativa", "Rango_edad")) %>%
(Population_FEntityWS <- rename( "Población Total" = "Población.x", "PoblaciónM" = "Población.y", "PoblaciónF" = "Población"))
# A tibble: 448 x 5
`Entidad Federativa` Rango_edad `Población Total` PoblaciónM PoblaciónF
<chr> <chr> <dbl> <dbl> <dbl>
1 Aguascalientes 0 a 4 109853 51587. 56336
2 Baja California 0 a 4 268842 124995. 127107.
3 Baja California Sur 0 a 4 46070 22289. 22569
4 Campeche 0 a 4 63034. 37749. 32170
5 Coahuila 0 a 4 244624. 104304. 115765
6 Colima 0 a 4 45985 34496. 23751.
7 Chiapas 0 a 4 491525 170085. 227851
8 Chihuahua 0 a 4 253834. 122434. 122055
9 Distrito Federal 0 a 4 682914. 462096. 366480
10 Durango 0 a 4 150485 76977. 68470
# … with 438 more rows
- Por rango de edad ¿Qué sexo predomina en cada población?
%>%
Population_FEntityWS mutate(PPredominante = if_else(PoblaciónM>=PoblaciónF, "M", "F")) %>%
select(`Entidad Federativa`, Rango_edad, PPredominante)
# A tibble: 448 x 3
`Entidad Federativa` Rango_edad PPredominante
<chr> <chr> <chr>
1 Aguascalientes 0 a 4 F
2 Baja California 0 a 4 F
3 Baja California Sur 0 a 4 F
4 Campeche 0 a 4 M
5 Coahuila 0 a 4 F
6 Colima 0 a 4 M
7 Chiapas 0 a 4 F
8 Chihuahua 0 a 4 M
9 Distrito Federal 0 a 4 M
10 Durango 0 a 4 M
# … with 438 more rows
- Por rango de edad ¿Qué sexo predomina y en que estado se encuentra dicho resultado?
%>%
Population_FEntityWS mutate(SPredominante = if_else(PoblaciónM>=PoblaciónF, "M", "F")) %>%
mutate(S = if_else(SPredominante == "M", PoblaciónM, PoblaciónF)) %>%
group_by(Rango_edad) %>%
top_n(1, S) %>% select(`Entidad Federativa`, Rango_edad, SPredominante)
# A tibble: 14 x 3
# Groups: Rango_edad [14]
`Entidad Federativa` Rango_edad SPredominante
<chr> <chr> <chr>
1 México 0 a 4 M
2 México 5 a 9 M
3 México 10 a 14 M
4 México 15 a 19 F
5 México 20 a 24 F
6 México 25 a 29 F
7 México 30 a 34 F
8 México 35 a 39 F
9 México 40 a 44 F
10 México 45 a 49 F
11 México 50 a 54 F
12 México 55 a 59 F
13 Distrito Federal 60 y más F
14 México NE M
- ¿Cuál estado tiene el mayor número de mujeres y cuál el de hombres?
Population_FEntityWS %>% group_by(`Entidad Federativa`) %>%
Population_by_sex_Mexico <- summarise(SMasculino = sum(PoblaciónM),
SFemenino = sum(PoblaciónF))
full_join(
%>% arrange(desc(SMasculino)) %>%
Population_by_sex_Mexico head(1) %>% select(-SFemenino),
%>% arrange(desc(SFemenino)) %>%
Population_by_sex_Mexico head(1) %>% select(-SMasculino),
by = "Entidad Federativa")
# A tibble: 1 x 3
`Entidad Federativa` SMasculino SFemenino
<chr> <dbl> <dbl>
1 México 6871269. 7305419
¿Cuál es el rango de edad con mayor población en mujeres y hombres?
Population_FEntityWS %>%
Population_by_sex_age_Mexico <- group_by(Rango_edad) %>%
summarise(PoblaciónF = sum(PoblaciónF), PoblaciónM = sum(PoblaciónM))
full_join(
%>% arrange(desc(PoblaciónM)) %>%
Population_by_sex_age_Mexico head(1) %>% select(-PoblaciónF),
%>% arrange(desc(PoblaciónF)) %>%
Population_by_sex_age_Mexico head(1) %>% select(-PoblaciónM),
by = "Rango_edad")
# A tibble: 1 x 3
Rango_edad PoblaciónM PoblaciónF
<chr> <dbl> <dbl>
1 10 a 14 5881838. 5790366
Finalmente, algunos puntos extra
- Recordando los cinco problemas en unos datos sin limpieza, dos que quedaron pendientes
- Múltiples tipos de observaciones están en la misma tabla.
- Una sola observación esta en múltiples tablas.
En el ejemplo que se ha tomado de la población mexicana tiene el primer defecto, ya que la población total bien podría quedar en una sola tabla junto con el nombre de la entidad y en otra los porcentajes de acuerdo a los rangos de edad. En general en este punto se debe tener en cuenta que cada unidad observacional debe tener su propia tabla, así logrando un técnica en bases de datos llamada normalización. Para este caso, se tendrían dos tablas
readxl::read_xls("Data/Population_Sex_FEntity.xls", range = "B5:Q38", col_names = TRUE)
Population_FEntity <-colnames(Population_FEntity)[1:2] <- c("Entidad Federativa", "Población Total")
Population_FEntity %>% gather(key = "Rango_edad",
Population_FEntity <-value = "Porcentaje", -c("Entidad Federativa", "Población Total")) %>%
mutate(Porcentaje = Porcentaje/100) %>%
mutate("Población" = Porcentaje * `Población Total`)
Population_FEntity %>% select(`Entidad Federativa`, `Población Total`)) (t1 <-
# A tibble: 462 x 2
`Entidad Federativa` `Población Total`
<chr> <dbl>
1 República Mexicana 103498524
2 Aguascalientes 1066233
3 Baja California 2856361
4 Baja California Sur 512030
5 Campeche 755703
6 Coahuila 2501413
7 Colima 568642
8 Chiapas 4293414
9 Chihuahua 3241513
10 Distrito Federal 8737172
# … with 452 more rows
Population_FEntity %>% select(`Entidad Federativa`, Rango_edad, Población)) (t2 <-
# A tibble: 462 x 3
`Entidad Federativa` Rango_edad Población
<chr> <chr> <dbl>
1 República Mexicana 0 a 4 9637687
2 Aguascalientes 0 a 4 109853
3 Baja California 0 a 4 268842
4 Baja California Sur 0 a 4 46070
5 Campeche 0 a 4 63034.
6 Coahuila 0 a 4 244624.
7 Colima 0 a 4 45985
8 Chiapas 0 a 4 491525
9 Chihuahua 0 a 4 253834.
10 Distrito Federal 0 a 4 682914.
# … with 452 more rows
Para el último punto se entiende que los datos están dispersos entre distintos conjuntos de datos, por lo que usar las funciones para combinar bases de datos ayudan en la resolución de este problema.
Es recomendable estudiar el paquete
purrr
, el cual es parte del tidyverse y ofrece una versión ideal para trabajar con funciones anónimas en data frames, ya que al tratar de aplicar alguna función de la familia apply con un data frame estos son convertidos primero a una matriz lo cual hace perder muchos de sus atributos. Así, con las funciones de la “familia”purrr::map
se pueden hacer todas las aplicaciones que se pueden hacer con alguna de la familiaapply
e incluso algunas más. Además de que tienen una aplicación ideal condplyr
. Véase Functional Programming.Se pueden usar las librerías
dtplyr
,dbplyr
osparklyr
como interfaz para usar la sintaxis dedplyr
en data.tables, bases de datos en SQL o en Apache Spark.Recordar que la función
dplyr::near()
compara dos elementos numéricos en punto flotante.La función
dplyr::case_when()
es una versión equivalente adplyr::if_else()
pero más general
tibble(x = 1:5) %>% mutate(new_variable = dplyr::case_when(x == 1 ~ "Hola", x == 5 ~ "Adios", TRUE ~ as.character(x)))
# A tibble: 5 x 2
x new_variable
<int> <chr>
1 1 Hola
2 2 2
3 3 3
4 4 4
5 5 Adios
dplyr
proviene de la paqueteríaplyr
la cual contiene muchas más funciones que pueden ser de utilidad en ciertas ocasiones.
Ejercicios
Utiliza la función
dplyr::top_n()
.En la siguiente página se puede aprender sobre el uso de expresiones regulares. Usando las funciones
dplyr::select()
ydplyr::matches()
replica los resultados que se dieron como ejemplo cuando se explico la funcióndplyr::select()
.Crea un data frame o tibble con 5 variables numéricas. Utiliza la función
dplyr::mutate_each()
para aplicar la funcióndplyr::percent_rank()
en cada una de las variables.Investigar algunas funciones de
purrr
comoreduce()
,map()
yaccumulate()
.Investiga el uso de
dplyr::semi_join()
ydplyr::anti_join()
.Si eres meticuloso, verás que algo anda en los datos de la población. ¿Por qué? Hint: Ve la tabla que se uso para los últimos ejemplos de la sección
dplyr
.Obtén resultados necesarios para contestar las siguientes preguntas sobre la base de datos
population
que ofrecedplyr
.
- ¿Cuantos años están registrados por país?
- ¿Qué países tienen mayor población por año?
- ¿Cuál es la edad promedio, con los años dados, de la población en cada país?
- ¿Qué países son aquellos que tienen más de un millón de habitantes en algún año?
- ¿Qué países tienen menos de un millón de habitantes en al menos un año?
- ¿Que país ha sido el que ha tenido la peor reducción en su población y en que año?
- ¿Qué país tuvo la mayor cantidad población en el año de tu nacimiento?
- ¿Cuál fue el año en que cada país tuvo su mayor numero de habitantes?
- ¿Cuál fue el año en que cada país tuvo su menor numero de habitantes?
- ¿Cuántos países considera la base de datos?
- La base de datos
who
dedplyr
contiene la información sobre los reportes de tuberculosis en el mundo. Haz una limpieza en esta base utilizando las funciones detidyr
y obtén la siguiente información.
- Por país ¿Cuál fue el año con mayor número de enfermos por tuberculosis?
- Por país ¿Cuál de los tres diagnósticos diferentes tiene una mayor ocurrencia?
- ¿Cuál fue el año con mayor número de enfermos por país?
- ¿Qué país ha tenido el mayor número de enfermos por tuberculosis?
- ¿Cuál es el top-10 de países con mayor número de casos con tuberculosis?
- ¿Hay algún país sin casos de tuberculosis en los años registrados?
- ¿Cuál sexo es el más afectado, por país, con tuberculosis?
- ¿Cuál es el rango de edad con mayor número de casos con tuberculosis?
- ¿Cuál es el rango de edad con menor número de casos con tuberculosis?
- Por rango de edad ¿Qué sexo es el más afectado con tuberculosis?
Las bases de datos
who
ypopulation
están relacionadas, por lo que se pueden unir para responder lo siguiente: ¿Existen países en los que el año donde se tuvo el máximo número de enfermos por tuberculosis sean los años donde se obtuvo un gran descenso en la población?La base de datos
storms
proporcionada pordplyr
contiene la información acerca de 198 tormentas tropicales con diferentes atributos. Obtén la siguiente información.
- ¿Cuántos huracanes tuvieron las tres clasificaciones (Depresión Tropical, Tormenta Tropical y Huracán)?
- ¿Cuál año fue el que tuvo el mayor número de huracanes?
- ¿Cuál año fue el que tuvo el mayor número de Tormentas Tropicales?
- ¿Cuál es el mes con mayor actividad?
- ¿Qué tormenta ha sido la que tuvo mayor número de cambios en su categoría Saffir-Simpson?
- ¿Cuál ha sido la tormenta con mayor velocidad promedio del viento en nudos?
- ¿Por tormenta, cuál ha sido la velocidad máxima que alcanzo en \(k/h\)?
- Considerando que una tormenta se puede clasificar por el estatus que predomina en ella, ¿Qué tipo de tormenta es la más recurrente en el Atlántico?
- ¿Cuál ha sido la tormenta con mayor duración?
- Considerando latitud y longitud en términos enteros (pensando así en segmentos del territorio), ¿Cuál ha sido la ubicación donde se ha tenido la mayor cantidad de tormentas?
- Utiliza las funciones Join que proporciona
dplyr
con las bases de datosdplyr::band_instruments
ydplyr::and_members
.