Aprendimos a cargar datos, pero es raro que los datos esten preparados para empezar nuestro análisis, así que hay que "arreglar/limpiar" los datos. Para ello, tenemos que:
Aprendimos a cargar datos, pero es raro que los datos esten preparados para empezar nuestro análisis, así que hay que "arreglar/limpiar" los datos. Para ello, tenemos que:
1) hacer nuestros datos TIDY,
2) arreglarlos para que sean útiles para nuestros propósitos.
Aprenderemos a limpiar y transformar datos en R. Priorizaremos la nueva forma de hacer las cosas en R (o workflow) conocido como tidyverse.
El procesado/limpieza de los datos suele ocupar un 80% del tiempo de un análisis de datos; así que el workflow sería más bien así:
Aprenderemos a limpiar y transformar datos en R. Priorizaremos la nueva forma de hacer las cosas en R (o workflow) conocido como tidyverse.
El procesado/limpieza de los datos suele ocupar un 80% del tiempo de un análisis de datos; así que el workflow sería más bien así:
Classroom data are like teddy bears; real data are like a grizzly with salmon blood dripping out its mouth. —- [@JennyBryan]
Conjunto de paquetes que trabajan en armonía y que permiten una nueva forma de escribir/programar en R.
tidyr
: convertir a tidy data
dplyr
: para manipular datos
ggplot2
: para hacer gráficos
readr
: para importar datostibble
: data frames actualizados forcast
: para manipular factoresstringr
: para manipular stringspurrr
: functional programming
Nos centraremos en los tres primeros paquetes, principalmente en dplyr
y ggplot2
.
Programs must be written for people to read, and only incidentally for machines to execute -- Hal Abelson
Dos principios del tidyverse:
Los scripts deben ser "fácilmente" legibles por las personas
Resolver problemas complejos encadenando funciones simples con el operador pipe (%>%
)
|>
. Puedes leer sobre ella aquíThe pipe es un operador que pasa el elemento que está a su izquierda como un argumento de la función que tiene a la derecha. That's all!!!
Con expresiones el operador pipe hace:
f(object, args. of the f.) ... es equivalente a ... object %>% f(args. of the f.)
library(palmerpenguins)head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
¿Qué hace la siguiente expresión?
4 %>% head(penguins, .)
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
¿Qué hace la siguiente expresión?
4 %>% head(penguins, .)
Y, ¿por qué no funciona la siguiente expresión?
4 %>% head(penguins)
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
¿Qué hace la siguiente expresión?
4 %>% head(penguins, .)
Y, ¿por qué no funciona la siguiente expresión?
4 %>% head(penguins)
Intenta descubrir/entender que hace la siguiente expresión:
letters %>% paste0( "-----" , . , "!!!" ) %>% toupper
Para trabajar à la tidyverse es crucial que los datos sean "tidy". El concepto de datos tidy es sencillo. Hacer los datos tidy no tanto pero tenemos un paquete para hacerlo fácil: el pkg tidyr
A dataset is a collection of values. Every value belongs to a variable and an observation.
data_1 <- data.frame( year = c("2014", "2015", "2016"), Pedro = c(100, 500, 200), Carla = c(400, 600, 250), María = c(200, 700, 900) )
data_2 <- data.frame(names = c("Pedro", "Carla", "María"), W_2014 = c(100, 400, 200), W_2015 = c(500, 600, 700), W_2016 = c(200, 250, 900) )
knitr::kable(data_2)
names | W_2014 | W_2015 | W_2016 |
---|---|---|---|
Pedro | 100 | 500 | 200 |
Carla | 400 | 600 | 250 |
María | 200 | 700 | 900 |
data_3 <- data.frame( names =rep(c("Pedro", "Carla", "María"), times = 3), year = rep(c("2014", "2015", "2016"), each = 3), salario = c(100, 400, 200, 500, 600, 700, 200, 250,900) )
gt::gt(data_3)
names | year | salario |
---|---|---|
Pedro | 2014 | 100 |
Carla | 2014 | 400 |
María | 2014 | 200 |
Pedro | 2015 | 500 |
Carla | 2015 | 600 |
María | 2015 | 700 |
Pedro | 2016 | 200 |
Carla | 2016 | 250 |
María | 2016 | 900 |
pivot_longer()
Aquí tienes un df en formato ANCHO, pásalo a formato LARGO
data_2 <- data.frame(names = c("Pedro", "Carla", "María"), W_2014 = c(100, 400, 200), W_2015 = c(500, 600, 700), W_2016 = c(200, 250, 900) )data_wide <- data_2
#- la función pivot_longer() transforma los datos de formato ancho(wide) a formato largo(long)data_long <- data_wide %>% tidyr::pivot_longer(cols = 2:4, names_to = "periodo")
separate()
y unite()
separate()
y unite()
facilitan el separar y unir columnas. Por ejemplo fíjate en el siguiente dataframe:names | year |
---|---|
Pedro_Navaja | 1978 |
Bob_Dylan | 1941 |
Cid_Campeador | 1048 |
separate()
y unite()
separate()
y unite()
facilitan el separar y unir columnas. Por ejemplo fíjate en el siguiente dataframe:names | year |
---|---|
Pedro_Navaja | 1978 |
Bob_Dylan | 1941 |
Cid_Campeador | 1048 |
df_a <- df %>% separate(col = names, into = c("Nombre", "Apellido"), sep = "_")
gt::gt(df_a)
Nombre | Apellido | year |
---|---|---|
Pedro | Navaja | 1978 |
Bob | Dylan | 1941 |
Cid | Campeador | 1048 |
df_b <- df_a %>% unite(Nombre_y_Apellido, Nombre:Apellido, sep = "&")
gt::gt(df_b)
Nombre_y_Apellido | year |
---|---|
Pedro&Navaja | 1978 |
Bob&Dylan | 1941 |
Cid&Campeador | 1048 |
dplyr
es un paquete que permite manipular datos de forma intuitiva. Tiene 6-7 funciones o verbos principales.
Cada uno de ellos hace “una sola cosa”, así que para realizar transformaciones complejas hay que ir concatenando instrucciones sencillas con el operador pipe (%>%
)
dplyr
es un paquete que permite manipular datos de forma intuitiva. Tiene 6-7 funciones o verbos principales.
Cada uno de ellos hace “una sola cosa”, así que para realizar transformaciones complejas hay que ir concatenando instrucciones sencillas con el operador pipe (%>%
)
Todas las funciones tienen una estructura o comportamiento similar:
filter(df, X1 >= 10)df %>% filter(. , X1 >= 10)df %>% filter(X1 >= 10)
filter()
: selecciona filas (que cumplen una o varias condiciones)arrange()
: reordena las filasrename()
: cambia los nombres de las columnas select()
: selecciona columnas mutate()
: crea nuevas variablessummarise()
: resume (colapsa) unos cuantos valores a uno sólo. Por ejemplo, calcula la media, moda, etc... de un conjunto de valoresfilter()
: selecciona filas (que cumplen una o varias condiciones)arrange()
: reordena las filasrename()
: cambia los nombres de las columnas select()
: selecciona columnas mutate()
: crea nuevas variablessummarise()
: resume (colapsa) unos cuantos valores a uno sólo. Por ejemplo, calcula la media, moda, etc... de un conjunto de valoresgroup_by()
: permite agrupar filas en función de una o varias condicionesfilter()
: selecciona filas (que cumplen una o varias condiciones)arrange()
: reordena las filasrename()
: cambia los nombres de las columnas select()
: selecciona columnas mutate()
: crea nuevas variablessummarise()
: resume (colapsa) unos cuantos valores a uno sólo. Por ejemplo, calcula la media, moda, etc... de un conjunto de valoresgroup_by()
: permite agrupar filas en función de una o varias condicionesdplyr 1.0.0
, en mayo de 2020, dos funciones más:across()
y where()
. Estas funciones son un poco diferentes, solo se usan en combinación de otra función/verbo. Son 2 funciones que en la jerga del tidyverse no son verbos sino adverbios. Lo vemosgapminder <- gapminder::gapminder #- cargamos los datos
gapminder <- gapminder::gapminder #- cargamos los datos
Por supuesto: hace accesibles, carga en memoria de R, los datos de gapminder.
El conjunto de datos gapminder está en el pkg gapminder
filter()
: permite seleccionar filas
Filas que cumplan una determinadas condiciones o criterios lógicos. Por ejemplo:
gapminder <- gapminder::gapminder #- cargamos los datos#- Observaciones de España (country == "Spain")aa <- gapminder %>% filter(country == "Spain") #- filas con valores de "lifeExp" < 29aa <- gapminder %>% filter(lifeExp < 29) #- filas con valores de "lifeExp" entre [29, 32]aa <- gapminder %>% filter(lifeExp >= 29 , lifeExp <= 32) aa <- gapminder %>% filter(lifeExp >= 29 & lifeExp <= 32) aa <- gapminder %>% filter(between(lifeExp, 29, 32)) #- observaciones de países de África con lifeExp > 32aa <- gapminder %>% filter(lifeExp > 72 & continent == "Africa") #- observaciones de países de África o Asia con lifeExp > 32aa <- gapminder %>% filter(lifeExp > 72 & continent %in% c("Africa", "Asia") ) aa <- gapminder %>% filter(lifeExp > 72 & (continent == "Africa" | continent == "Asia") )
slice()
: permite seleccionar filas pero por posición.filter()
y slice()
ambas seleccionan filas, la primera por condiciones y la segunda por posición:#- selecciona las observaciones de la décima a la quinceavaaa <- gapminder %>% slice(c(10:15)) #- selecciona las observaciones de la 12 a 14 Y de la 44 a 46, Y las 4 últimasaa <- gapminder %>% slice( c(12:14, 44:46, n()-4:n()) ) #- AQUI hay un error, tenéis que arreglarlo. #- Pista: igual os ayuda crear una columna con el índice de rows y repetir el cálculoaa <- gapminder %>% mutate(index = 1:n())aa <- gapminder %>% slice( c(12:14, 44:46, n()-4:n()) )
slice()
slice_max()
y slice_min()
: seleccionan filas con valor máximo (o mínimo) de una variable: #- selecciona las 3 filas con mayor valor de lifeExpaa <- gapminder %>% slice_max(lifeExp, n = 3)#- selecciona las 4 filas con MENOR valor de popaa <- gapminder %>% slice_min(pop, n = 4)#- observaciones en el primer decil en cuanto a esperanza de vida, 10% con menor esperanza de vidaaa <- gapminder %>% slice_min(lifeExp, prop = 0.1)#- 1% de observaciones con mayor población. Imagino que estarán China e Indiaaa <- gapminder %>% slice_max(pop, prop = 0.01)
slice()
slice_max()
y slice_min()
: seleccionan filas con valor máximo (o mínimo) de una variable: #- selecciona las 3 filas con mayor valor de lifeExpaa <- gapminder %>% slice_max(lifeExp, n = 3)#- selecciona las 4 filas con MENOR valor de popaa <- gapminder %>% slice_min(pop, n = 4)#- observaciones en el primer decil en cuanto a esperanza de vida, 10% con menor esperanza de vidaaa <- gapminder %>% slice_min(lifeExp, prop = 0.1)#- 1% de observaciones con mayor población. Imagino que estarán China e Indiaaa <- gapminder %>% slice_max(pop, prop = 0.01)
A veces se necesita obtener una muestra aleatoria de los datos: por ejemplo con slice_sample()
:
#- selecciona (aleatoriamente) 100 filas de los datosaa <- gapminder %>% slice_sample(n = 100)#- selecciona (aleatoriamente) un 5% de los datosaa <- gapminder %>% slice_sample(prop = 0.05)
arrange()
: permite reordenar las filas de un df#- ordena las filas de MENOR a mayor según los valores de la v. lifeExp aa <- gapminder %>% arrange(lifeExp)#- ordena las filas de MAYOR a menor según los valores de la v. lifeExpaa <- gapminder %>% arrange(desc(lifeExp)) #- ordena las filas de MENOR a mayor según los valores de la v. lifeExp. #- Si hay empates se resuelve con la variable "pop"aa <- gapminder %>% arrange(lifeExp, pop)
rename()
: permite cambiar los nombres de las variables#- cambia los nombres de lifeExp y gdpPercap a life_exp y gdp_percap gapminder %>% rename(life_exp = lifeExp, gdp_percap = gdpPercap)
names()
es útil#-(!!) la función names() de R-base es muy útil. aa <- gapmindernames(aa) <- names(aa) %>% touppernames(aa) <- names(aa) %>% tolower
rename_with()
permite hacer transformaciones más complejas [🌶]aa <- gapminderaa %>% rename_with(toupper)rename_with(aa, toupper, starts_with("Life") | contains("countr"))rename_with(aa, ~ str_replace(.x, "e", "Ö")) #- (!!!!)
select()
se utiliza para seleccionar variablesaa <- gapminder %>% select(year, lifeExp)
aa <- gapminder %>% select(1:3, 5)
select()
se utiliza para seleccionar variablesaa <- gapminder %>% select(year, lifeExp)
aa <- gapminder %>% select(1:3, 5)
aa <- gapminder %>% select(-year)#- Para eliminar varias variablesaa <- gapminder %>% select(-c(year, lifeExp))
aa <- gapminder %>% select(-c(1:3, 5))
select()
junto a la función where()
select()
y where()
son dos funciones, sí, pero en la jerga del tidyverse, select()
es un verbo y where()
es un adverbio, cualifica/cambia lo que hace select()
.select()
junto a la función where()
select()
y where()
son dos funciones, sí, pero en la jerga del tidyverse, select()
es un verbo y where()
es un adverbio, cualifica/cambia lo que hace select()
.En gapminder
las 2 primeras variables (country y continent) son factores y las 4 siguientes son variable numéricas.
Imagina que queremos seleccionar sólo las variables que son numéricas. Podemos hacerlo por nombre o por posición pero mejor con select()
y la función auxiliar where()
aa <- gapminder %>% select(is.numeric) #- funciona, pero ...aa <- gapminder %>% select(where(is.numeric)) #- es "preferible" esta segunda expresión
select()
junto a la función where()
select()
y where()
son dos funciones, sí, pero en la jerga del tidyverse, select()
es un verbo y where()
es un adverbio, cualifica/cambia lo que hace select()
.En gapminder
las 2 primeras variables (country y continent) son factores y las 4 siguientes son variable numéricas.
Imagina que queremos seleccionar sólo las variables que son numéricas. Podemos hacerlo por nombre o por posición pero mejor con select()
y la función auxiliar where()
aa <- gapminder %>% select(is.numeric) #- funciona, pero ...aa <- gapminder %>% select(where(is.numeric)) #- es "preferible" esta segunda expresión
aa <- gapminder %>% select(!where(is.numeric))
select()
para renombrar y reordenar las variables#- dejamos en aa solamente a las columnas "year" y "pop"; ADEMÁS, ahora, "pop" irá antes que "year"aa <- gapminder %>% select(pop, year)#- dejamos en aa solamente a las columnas "year" y "pop" y les cambiamos el nombreaa <- gapminder %>% select(poblacion = pop, año = year)
Imagina que quieres que la última columna pase a ser la primera (manías!!). Podemos hacerlo con select y everything()
. everything es una función auxiliar:
#- "gdpPercap" que es la última columna pasa a ser la primeraaa <- gapminder %>% select(gdpPercap, everything())
select()
para renombrar y reordenar las variables#- dejamos en aa solamente a las columnas "year" y "pop"; ADEMÁS, ahora, "pop" irá antes que "year"aa <- gapminder %>% select(pop, year)#- dejamos en aa solamente a las columnas "year" y "pop" y les cambiamos el nombreaa <- gapminder %>% select(poblacion = pop, año = year)
Imagina que quieres que la última columna pase a ser la primera (manías!!). Podemos hacerlo con select y everything()
. everything es una función auxiliar:
#- "gdpPercap" que es la última columna pasa a ser la primeraaa <- gapminder %>% select(gdpPercap, everything())
relocate()
otra función para reordenar las variables de un dfaa <- gapminder %>% dplyr::relocate(country, .after = lifeExp)aa <- gapminder %>% dplyr::relocate(country, .before = lifeExp)
mutate()
para crear nuevas variablesaa <- gapminder %>% mutate(GDP = pop*gdpPercap)
Por defecto, la nueva variable creada se situará al final del df, a no ser que usemos los argumentos .after
y .before
[🌶]
aa <- gapminder %>% mutate(GDP = pop*gdpPercap, .after = country)aa <- gapminder %>% mutate(GDP = pop*gdpPercap, .before = country)
summarize()
para "resumir" variablesCoge una variable como input y devuelve un solo valor; por ejemplo, haya la media aritmética (o el mínimo, o el máximo ...) de una columna/variable.
Empezamos "resumiendo" una sola variable:
aa <- gapminder %>% summarise(media = mean(lifeExp)) aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) aa <- gapminder %>% summarise(max(pop)) aa <- gapminder %>% summarise(NN = n()) aa <- gapminder %>% count() #- más adelante veremos la utilidad de count()
summarize()
para "resumir" variablesCoge una variable como input y devuelve un solo valor; por ejemplo, haya la media aritmética (o el mínimo, o el máximo ...) de una columna/variable.
Empezamos "resumiendo" una sola variable:
aa <- gapminder %>% summarise(media = mean(lifeExp)) aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) aa <- gapminder %>% summarise(max(pop)) aa <- gapminder %>% summarise(NN = n()) aa <- gapminder %>% count() #- más adelante veremos la utilidad de count()
#- retornará 2 valores: las medias de "lifeExp" y "gdpPercap"aa <- gapminder %>% summarise(mean(lifeExp), mean(gdpPercap))
summarize()
para "resumir" variablesCoge una variable como input y devuelve un solo valor; por ejemplo, haya la media aritmética (o el mínimo, o el máximo ...) de una columna/variable.
Empezamos "resumiendo" una sola variable:
aa <- gapminder %>% summarise(media = mean(lifeExp)) aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) aa <- gapminder %>% summarise(max(pop)) aa <- gapminder %>% summarise(NN = n()) aa <- gapminder %>% count() #- más adelante veremos la utilidad de count()
#- retornará 2 valores: las medias de "lifeExp" y "gdpPercap"aa <- gapminder %>% summarise(mean(lifeExp), mean(gdpPercap))
#- retornará 2 valores: la media y sd de la v. "lifeExp"aa <- gapminder %>% summarise(mean(lifeExp), sd(lifeExp))
summarize()
con across()
Usar across()
permitirá calcular estadísticos de todas las variables, o de subconjuntos de estas, de manera más cómoda:
#- media de cada una de las 6 variables. Devuelve 2 warnings porque las 2 primeras son textuales. No se puede calcular la media de continent y countrygapminder %>% summarise(across(everything(), mean) ) #- calculamos la media de tercera a la sexta variablegapminder %>% summarise(across(3:6, mean) )
across()
permite seleccionar variables para hacer summaries. summarize()
con across()
Usar across()
permitirá calcular estadísticos de todas las variables, o de subconjuntos de estas, de manera más cómoda:
#- media de cada una de las 6 variables. Devuelve 2 warnings porque las 2 primeras son textuales. No se puede calcular la media de continent y countrygapminder %>% summarise(across(everything(), mean) ) #- calculamos la media de tercera a la sexta variablegapminder %>% summarise(across(3:6, mean) )
across()
permite seleccionar variables para hacer summaries. across()
se puede utilizar where()
para aplicar criterios lógicos para seleccionar variables: [🌶] [ 🌟 ]gapminder %>% summarise(across(where(is.numeric), mean)) #- con los nombres de los argumentos (más largo pero conviene verlo de vez en cuando)gapminder %>% summarise(across(.cols = where(is.numeric), .fns = mean))
summarize()
con across()
y varias funciones(list)summarise()
con across()
pero, además, tendrás que poner la lista de funciones dentro de list()
. [🌶] [ 🌟 ] #- calculamos la media y desviación típica de las columnas 3 a 6.gapminder %>% summarise(across(3:6, list(media = mean, desv = sd)))
#- lo mismo, pero explicitando los nombres de los argumentos [🌶] gapminder %>% summarise(across(.cols = 3:6, .fns = list(media = mean, desv = sd) ))#- lo mismo otra vez, pero eligiendo el nombre de las variables que se van a crear con .names [🌶] [🌶] gapminder %>% summarise(across(3:6, list(media = mean, desv = sd), .names = "{fn}_{col}"))
group_by()
. Con está función ya se puede ver la potencia de dplyr
En análisis de datos muchas operaciones queremos calcularlas para distintos grupos (p. ej. mujer/hombre, ...). group_by()
permite hacerlo.
group_by()
coge un df y lo convierte en un "df agrupado". En ese nuevo "df agrupado", las operaciones que hagamos, se harán por separado para cada uno de los grupos que hayamos definido. Ahora lo vemos.
group_by()
. Con está función ya se puede ver la potencia de dplyr
En análisis de datos muchas operaciones queremos calcularlas para distintos grupos (p. ej. mujer/hombre, ...). group_by()
permite hacerlo.
group_by()
coge un df y lo convierte en un "df agrupado". En ese nuevo "df agrupado", las operaciones que hagamos, se harán por separado para cada uno de los grupos que hayamos definido. Ahora lo vemos.
#- cogemos df y lo (des)agrupamos por grupos #- definidos por la variable "continent"; o sea, habrá 5 grupos#- después con summarise() calcularemos el nº de observaciones en cada grupo;#- es decir, nos retornará un df con una fila por cada continenteaa <- gapminder %>% group_by(continent) %>% summarise(NN = n()) aa#> # A tibble: 5 × 2#> continent NN#> <fct> <int>#> 1 Africa 624#> 2 Americas 300#> 3 Asia 396#> 4 Europe 360#> 5 Oceania 24
group_by()
Pregunta: ¿cuantos países hay en la base de datos?
aa <- gapminder %>% group_by(country) %>% summarize(NN = n())
group_by()
Pregunta: ¿cuantos países hay en la base de datos?
aa <- gapminder %>% group_by(country) %>% summarize(NN = n())
#- cogemos df y lo agrupamos por "continent", #- después calculamos 2 cosas: el número de observaciones o rows#- y el número de países en cada continente (NN_countries)aa <- gapminder %>% group_by(continent) %>% summarize(NN = n(), NN_countries = n_distinct(country)) aa#> # A tibble: 5 × 3#> continent NN NN_countries#> <fct> <int> <int>#> 1 Africa 624 52#> 2 Americas 300 25#> 3 Asia 396 33#> 4 Europe 360 30#> 5 Oceania 24 2
group_by()
aa <- gapminder %>% group_by(continent) %>% summarize(NN = n(), NN_countries = length(unique(country)) )aa#> # A tibble: 5 × 3#> continent NN NN_countries#> <fct> <int> <int>#> 1 Africa 624 52#> 2 Americas 300 25#> 3 Asia 396 33#> 4 Europe 360 30#> 5 Oceania 24 2
dplyr
Primero algo sencillo para calentar: hay que calcular la esperanza de vida media por continente.
#- cogemos df y lo agrupamos por "continent", #- después calculamos la media de "lifeExp"gapminder %>% group_by(continent) %>% summarize(mean(lifeExp)) #> # A tibble: 5 × 2#> continent `mean(lifeExp)`#> <fct> <dbl>#> 1 Africa 48.9#> 2 Americas 64.7#> 3 Asia 60.1#> 4 Europe 71.9#> 5 Oceania 74.3
Calculemos la esperanza de vida media por continente en el primer periodo (1952)
#- cogemos df y filtramos para quedarnos con las observaciones de 1952#- después lo agrupamos por "continent", #- después calculamos la media de "lifeExp"gapminder %>% filter(year == "1952") %>% group_by(continent) %>% summarize(mean(lifeExp))
¿Qué hace este trozo de código?
gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(mean(lifeExp), mean(gdpPercap))
#- cogemos df y filtramos las observaciones de 1952 y 2007#- agrupamos por "continent", #- después calculamos la media de "lifeExp" y de "gdpPercap"gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(mean(lifeExp), mean(gdpPercap)) #> # A tibble: 10 × 4#> # Groups: continent [5]#> continent year `mean(lifeExp)` `mean(gdpPercap)`#> <fct> <int> <dbl> <dbl>#> 1 Africa 1952 39.1 1253.#> 2 Africa 2007 54.8 3089.#> 3 Americas 1952 53.3 4079.#> 4 Americas 2007 73.6 11003.#> 5 Asia 1952 46.3 5195.#> 6 Asia 2007 70.7 12473.#> 7 Europe 1952 64.4 5661.#> 8 Europe 2007 77.6 25054.#> 9 Oceania 1952 69.3 10298.#> 10 Oceania 2007 80.7 29810.
across()
#- Voy a crear un nuevo df: "gapminder_gr" o "gapminder agrupado"gapminder_gr <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) #- y sobre "gapminder_gr" iremos haciendo cálculos#- si queremos calcular la media de varias variables tenemos que usar across()gapminder_gr %>% summarise(across(c(lifeExp, gdpPercap), mean))#- si queremos calcular la media de todas las variables numéricas tenemos que usar across() y where()gapminder_gr %>% summarise(across(where(is.numeric), mean))#- si queremos calcular la media y la mediana, hay que usar list()gapminder_gr %>% summarise(across(c(lifeExp, gdpPercap), list (media = mean, mediana = median) ))#- si ponemos los nombres de los argumentos quedaría comogapminder_gr %>% summarise(across(.cols = c(lifeExp, gdpPercap), .fns = list (media = mean, mediana = median)))#- además, podemos controlar el nombre de las variables creadas con el argumento .namesgapminder_gr %>% summarise(across(c(lifeExp, gdpPercap), list (media = mean, mediana = median), .names = "{fn}_{col}"))
gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup()#> # A tibble: 10 × 3#> continent year media#> <fct> <int> <dbl>#> 1 Africa 1952 39.1#> 2 Africa 2007 54.8#> 3 Americas 1952 53.3#> 4 Americas 2007 73.6#> 5 Asia 1952 46.3#> 6 Asia 2007 70.7#> 7 Europe 1952 64.4#> 8 Europe 2007 77.6#> 9 Oceania 1952 69.3#> 10 Oceania 2007 80.7
#- se puede hacer de una vez, pero vamos a partir el código en 2 trozosaa <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup()aa1 <- aa %>% group_by(continent) %>% summarise(min_l = min(media), max_l = max(media)) %>% mutate(dif = max_l-min_l) %>% arrange(desc(dif))aa1#> # A tibble: 5 × 4#> continent min_l max_l dif#> <fct> <dbl> <dbl> <dbl>#> 1 Asia 46.3 70.7 24.4#> 2 Americas 53.3 73.6 20.3#> 3 Africa 39.1 54.8 15.7#> 4 Europe 64.4 77.6 13.2#> 5 Oceania 69.3 80.7 11.5
#- segundo intento: se puede hacer de una vez, pero vamos a partir el código en 2 trozosaa <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup() #- usamos lag()aa1 <- aa %>% group_by(continent) %>% arrange(year) %>% mutate(variac_l = media - lag(media))#- mostramos los resultadosaa1 %>% filter(year == 2007) %>% arrange(desc(variac_l))#> # A tibble: 5 × 4#> # Groups: continent [5]#> continent year media variac_l#> <fct> <int> <dbl> <dbl>#> 1 Asia 2007 70.7 24.4#> 2 Americas 2007 73.6 20.3#> 3 Africa 2007 54.8 15.7#> 4 Europe 2007 77.6 13.2#> 5 Oceania 2007 80.7 11.5
#- esta parte es comúnaa <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup()#- pero ahora usamos pivot_wider()aa %>% pivot_wider(names_from = year, values_from = media) %>% mutate(dif_l = 2007 - 1952) %>% arrange(desc(dif_l))#> # A tibble: 5 × 4#> continent `1952` `2007` dif_l#> <fct> <dbl> <dbl> <dbl>#> 1 Africa 39.1 54.8 55#> 2 Americas 53.3 73.6 55#> 3 Asia 46.3 70.7 55#> 4 Europe 64.4 77.6 55#> 5 Oceania 69.3 80.7 55
aa <- gapminder %>% group_by(continent, year) %>% select(continent, year, lifeExp) %>% summarise(mean_life = mean(lifeExp)) %>% arrange(year) %>% mutate(incre_mean_life_0 = mean_life - first(mean_life)) %>% mutate(incre_mean_life_t = mean_life - lag(mean_life)) %>% arrange(continent)#- por ejemplo veamos el resultado para Europeaa %>% filter(continent == "Europe")
En palabras de Jennyfer Bryan:
Break the code into pieces, starting at the top, and inspect the intermediate results. That’s certainly how I was able to write such a thing. These commands do not leap fully formed out of anyone’s forehead – they are built up gradually, with lots of errors and refinements along the way. Is the statement above really hard for you to read? If yes, then by all means break it into pieces and make some intermediate objects. Your code should be easy to write and read when you’re done.
#- variación de lifeExp en Spain año a año (bueno lustro a lustro)gapminder %>% group_by(country) %>% select(country, year, lifeExp) %>% mutate(lifeExp_gain_cada_lustro = lifeExp - lag(lifeExp)) %>% filter(country == "Spain" )#> # A tibble: 12 × 4#> # Groups: country [1]#> country year lifeExp lifeExp_gain_cada_lustro#> <fct> <int> <dbl> <dbl>#> 1 Spain 1952 64.9 NA #> 2 Spain 1957 66.7 1.72 #> 3 Spain 1962 69.7 3.03 #> 4 Spain 1967 71.4 1.75 #> 5 Spain 1972 73.1 1.62 #> 6 Spain 1977 74.4 1.33 #> 7 Spain 1982 76.3 1.91 #> 8 Spain 1987 76.9 0.600#> 9 Spain 1992 77.6 0.670#> 10 Spain 1997 78.8 1.20 #> 11 Spain 2002 79.8 1.01 #> 12 Spain 2007 80.9 1.16
¿Y la variación acumulada? Fácil!! Sólo tendríamos que sumar o acumular la variable "lifeExp_gain_cada_lustro" que hemos generado anteriormente, así que sólo habría que añadir una linea a nuestro código:
gapminder %>% group_by(country) %>% select(country, year, lifeExp) %>% mutate(lifeExp_gain_cada_lustro = lifeExp - lag(lifeExp)) %>% #- Al final para hacerlo (como había pensado) me han hecho falta 2 lineas, #- porque la primera observación de "lifeExp_gain_cada_lustro" es un NA #- y eso hacía que la función cumsum() no funcionase.mutate(lifeExp_gain_cada_lustro2 = `ifelse`(is.na(lifeExp_gain_cada_lustro), 0, lifeExp_gain_cada_lustro)) %>% mutate(lifeExp_gain_acumulado = cumsum(lifeExp_gain_cada_lustro2)) %>% filter(country == "Spain")
Otra solución, además es más fácil:
#- ganancia acumulada (otra forma de hacer lo mismo)gapminder %>% group_by(country) %>% select(country, year, lifeExp) %>% mutate(lifeExp_gain_acumulada = lifeExp - lifeExp[1]) %>% filter(country == "Spain")#> # A tibble: 12 × 4#> # Groups: country [1]#> country year lifeExp lifeExp_gain_acumulada#> <fct> <int> <dbl> <dbl>#> 1 Spain 1952 64.9 0 #> 2 Spain 1957 66.7 1.72#> 3 Spain 1962 69.7 4.75#> 4 Spain 1967 71.4 6.5 #> 5 Spain 1972 73.1 8.12#> 6 Spain 1977 74.4 9.45#> 7 Spain 1982 76.3 11.4 #> 8 Spain 1987 76.9 12.0 #> 9 Spain 1992 77.6 12.6 #> 10 Spain 1997 78.8 13.8 #> 11 Spain 2002 79.8 14.8 #> 12 Spain 2007 80.9 16.0
aa <- gapminder %>% filter(continent == "Asia") %>% select(year, country, lifeExp) %>% group_by(year) %>% slice_max(n = 3, lifeExp) %>% arrange(year)
Una función auxiliar que es muy útil al utilizarla junto a mutate: case_when()
.
aa <- gapminder %>% group_by(continent, year) %>% mutate(media_lifeExp = mean(lifeExp)) %>% mutate(media_gdpPercap = mean(gdpPercap)) %>% mutate(GOOD_or_BAD = case_when( lifeExp > mean(lifeExp) & gdpPercap > mean(gdpPercap) ~ "good", lifeExp < mean(lifeExp) & gdpPercap < mean(gdpPercap) ~ "bad" , lifeExp < mean(lifeExp) | gdpPercap < mean(gdpPercap) ~ "medium" )) %>% filter(country == "Spain")
bind_cols()
y bind_rows()
bind_cols()
(o con cbind() de R-base)df_1 <- iris[ , 1:2] ; df_2 <- iris[ , 3:5]df_1 <- iris %>% select(1:2) ; df_2 <- iris %>% select(3:5) df_3 <- bind_cols(df_1, df_2)identical(iris, df_3)
bind_cols()
y bind_rows()
bind_cols()
(o con cbind() de R-base)df_1 <- iris[ , 1:2] ; df_2 <- iris[ , 3:5]df_1 <- iris %>% select(1:2) ; df_2 <- iris %>% select(3:5) df_3 <- bind_cols(df_1, df_2)identical(iris, df_3)
bind_rows()
(o con rbind() de R-base)df_1 <- iris[1:75, ] ; df_2 <- iris[76:150, ]df_1 <- iris %>% slice(1:75) ; df_2 <- iris %>% slice(76:150) df_3 <- bind_rows(df_1, df_2)identical(iris, df_3)
En dplyr hay 3 tipos de funciones(verbos) que se ocupan de diferentes operaciones para unir datasets:
Mutating joins, añade nuevas variables (o columnas) a un dataframe (df1). Estas nuevas columnas vienen de un segundo df2 (hay varias mutating joins, dependiendo del criterio para seleccionar las filas)
Filtering joins, filtra las filas (observaciones) de un dataframe (df1) basándose en si las filas de df1 coinciden (match) o no con una observación del segundo df2
Set operations, combina las observaciones de los dos datasets (df1 y df2) as if they were set elements.
Hay 3/4 tipos de mutating joins. Su sintaxis es idéntica, sólo se diferencian en que las filas que se seleccionan dependen del criterio para hacer el match:
inner_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2, PERO solo retorna las filas de df1 que tienen una equivalencia en df2. (la equivalencia se define en función del valor de una variable o variables comunes en df1 y df2)left_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1. (Si hubiesen varios matches entre df1 e df2 se retornan todas las combinaciones!!!!) full_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1 y de df2. Osea, retorna TODAS las filas y TODAS las columnas de las 2 tablas. (Donde no hay matches retorna NA's)Hay 3/4 tipos de mutating joins. Su sintaxis es idéntica, sólo se diferencian en que las filas que se seleccionan dependen del criterio para hacer el match:
inner_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2, PERO solo retorna las filas de df1 que tienen una equivalencia en df2. (la equivalencia se define en función del valor de una variable o variables comunes en df1 y df2)left_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1. (Si hubiesen varios matches entre df1 e df2 se retornan todas las combinaciones!!!!) full_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1 y de df2. Osea, retorna TODAS las filas y TODAS las columnas de las 2 tablas. (Donde no hay matches retorna NA's)x <- tibble(id = 1:3, x = paste0("x", 1:3))y <- tibble(id = (1:4)[-3], y = paste0("y", (1:4)[-3]))
#- only includes observations that match in both x and ydf_inner <- inner_join(x, y)
#- full_join() includes all observations from x and ydf_full_join <- full_join(x, y)
#- includes all observations in x, regardless of whether they match or not. #- This is the most commonly used join because it ensures that you don’t lose observations from your primary table.df_left_join <- left_join(x, y)
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
o | Tile View: Overview of Slides |
s | Toggle scribble toolbox |
Alt + f | Fit Slides to Screen |
Esc | Back to slideshow |
Aprendimos a cargar datos, pero es raro que los datos esten preparados para empezar nuestro análisis, así que hay que "arreglar/limpiar" los datos. Para ello, tenemos que:
Aprendimos a cargar datos, pero es raro que los datos esten preparados para empezar nuestro análisis, así que hay que "arreglar/limpiar" los datos. Para ello, tenemos que:
1) hacer nuestros datos TIDY,
2) arreglarlos para que sean útiles para nuestros propósitos.
Aprenderemos a limpiar y transformar datos en R. Priorizaremos la nueva forma de hacer las cosas en R (o workflow) conocido como tidyverse.
El procesado/limpieza de los datos suele ocupar un 80% del tiempo de un análisis de datos; así que el workflow sería más bien así:
Aprenderemos a limpiar y transformar datos en R. Priorizaremos la nueva forma de hacer las cosas en R (o workflow) conocido como tidyverse.
El procesado/limpieza de los datos suele ocupar un 80% del tiempo de un análisis de datos; así que el workflow sería más bien así:
Classroom data are like teddy bears; real data are like a grizzly with salmon blood dripping out its mouth. —- [@JennyBryan]
Conjunto de paquetes que trabajan en armonía y que permiten una nueva forma de escribir/programar en R.
tidyr
: convertir a tidy data
dplyr
: para manipular datos
ggplot2
: para hacer gráficos
readr
: para importar datostibble
: data frames actualizados forcast
: para manipular factoresstringr
: para manipular stringspurrr
: functional programming
Nos centraremos en los tres primeros paquetes, principalmente en dplyr
y ggplot2
.
Programs must be written for people to read, and only incidentally for machines to execute -- Hal Abelson
Dos principios del tidyverse:
Los scripts deben ser "fácilmente" legibles por las personas
Resolver problemas complejos encadenando funciones simples con el operador pipe (%>%
)
|>
. Puedes leer sobre ella aquíThe pipe es un operador que pasa el elemento que está a su izquierda como un argumento de la función que tiene a la derecha. That's all!!!
Con expresiones el operador pipe hace:
f(object, args. of the f.) ... es equivalente a ... object %>% f(args. of the f.)
library(palmerpenguins)head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
¿Qué hace la siguiente expresión?
4 %>% head(penguins, .)
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
¿Qué hace la siguiente expresión?
4 %>% head(penguins, .)
Y, ¿por qué no funciona la siguiente expresión?
4 %>% head(penguins)
Estas 3 expresiones también son equivalentes, hacen exactamente lo mismo:
head(penguins, n = 4) #- forma habitual de llamar/usar la función head()penguins %>% head(. , n = 4) #- usando el operador pipe (con el punto actuando como placeholder)penguins %>% head(n = 4) #- usando el operador pipe (SIN el punto)
¿Qué hace la siguiente expresión?
4 %>% head(penguins, .)
Y, ¿por qué no funciona la siguiente expresión?
4 %>% head(penguins)
Intenta descubrir/entender que hace la siguiente expresión:
letters %>% paste0( "-----" , . , "!!!" ) %>% toupper
Para trabajar à la tidyverse es crucial que los datos sean "tidy". El concepto de datos tidy es sencillo. Hacer los datos tidy no tanto pero tenemos un paquete para hacerlo fácil: el pkg tidyr
A dataset is a collection of values. Every value belongs to a variable and an observation.
data_1 <- data.frame( year = c("2014", "2015", "2016"), Pedro = c(100, 500, 200), Carla = c(400, 600, 250), María = c(200, 700, 900) )
DT::datatable(data_1)
data_2 <- data.frame(names = c("Pedro", "Carla", "María"), W_2014 = c(100, 400, 200), W_2015 = c(500, 600, 700), W_2016 = c(200, 250, 900) )
knitr::kable(data_2)
names | W_2014 | W_2015 | W_2016 |
---|---|---|---|
Pedro | 100 | 500 | 200 |
Carla | 400 | 600 | 250 |
María | 200 | 700 | 900 |
data_3 <- data.frame( names =rep(c("Pedro", "Carla", "María"), times = 3), year = rep(c("2014", "2015", "2016"), each = 3), salario = c(100, 400, 200, 500, 600, 700, 200, 250,900) )
gt::gt(data_3)
names | year | salario |
---|---|---|
Pedro | 2014 | 100 |
Carla | 2014 | 400 |
María | 2014 | 200 |
Pedro | 2015 | 500 |
Carla | 2015 | 600 |
María | 2015 | 700 |
Pedro | 2016 | 200 |
Carla | 2016 | 250 |
María | 2016 | 900 |
pivot_longer()
Aquí tienes un df en formato ANCHO, pásalo a formato LARGO
data_2 <- data.frame(names = c("Pedro", "Carla", "María"), W_2014 = c(100, 400, 200), W_2015 = c(500, 600, 700), W_2016 = c(200, 250, 900) )data_wide <- data_2
#- la función pivot_longer() transforma los datos de formato ancho(wide) a formato largo(long)data_long <- data_wide %>% tidyr::pivot_longer(cols = 2:4, names_to = "periodo")
separate()
y unite()
separate()
y unite()
facilitan el separar y unir columnas. Por ejemplo fíjate en el siguiente dataframe:names | year |
---|---|
Pedro_Navaja | 1978 |
Bob_Dylan | 1941 |
Cid_Campeador | 1048 |
separate()
y unite()
separate()
y unite()
facilitan el separar y unir columnas. Por ejemplo fíjate en el siguiente dataframe:names | year |
---|---|
Pedro_Navaja | 1978 |
Bob_Dylan | 1941 |
Cid_Campeador | 1048 |
df_a <- df %>% separate(col = names, into = c("Nombre", "Apellido"), sep = "_")
gt::gt(df_a)
Nombre | Apellido | year |
---|---|---|
Pedro | Navaja | 1978 |
Bob | Dylan | 1941 |
Cid | Campeador | 1048 |
df_b <- df_a %>% unite(Nombre_y_Apellido, Nombre:Apellido, sep = "&")
gt::gt(df_b)
Nombre_y_Apellido | year |
---|---|
Pedro&Navaja | 1978 |
Bob&Dylan | 1941 |
Cid&Campeador | 1048 |
dplyr
es un paquete que permite manipular datos de forma intuitiva. Tiene 6-7 funciones o verbos principales.
Cada uno de ellos hace “una sola cosa”, así que para realizar transformaciones complejas hay que ir concatenando instrucciones sencillas con el operador pipe (%>%
)
dplyr
es un paquete que permite manipular datos de forma intuitiva. Tiene 6-7 funciones o verbos principales.
Cada uno de ellos hace “una sola cosa”, así que para realizar transformaciones complejas hay que ir concatenando instrucciones sencillas con el operador pipe (%>%
)
Todas las funciones tienen una estructura o comportamiento similar:
filter(df, X1 >= 10)df %>% filter(. , X1 >= 10)df %>% filter(X1 >= 10)
filter()
: selecciona filas (que cumplen una o varias condiciones)arrange()
: reordena las filasrename()
: cambia los nombres de las columnas select()
: selecciona columnas mutate()
: crea nuevas variablessummarise()
: resume (colapsa) unos cuantos valores a uno sólo. Por ejemplo, calcula la media, moda, etc... de un conjunto de valoresfilter()
: selecciona filas (que cumplen una o varias condiciones)arrange()
: reordena las filasrename()
: cambia los nombres de las columnas select()
: selecciona columnas mutate()
: crea nuevas variablessummarise()
: resume (colapsa) unos cuantos valores a uno sólo. Por ejemplo, calcula la media, moda, etc... de un conjunto de valoresgroup_by()
: permite agrupar filas en función de una o varias condicionesfilter()
: selecciona filas (que cumplen una o varias condiciones)arrange()
: reordena las filasrename()
: cambia los nombres de las columnas select()
: selecciona columnas mutate()
: crea nuevas variablessummarise()
: resume (colapsa) unos cuantos valores a uno sólo. Por ejemplo, calcula la media, moda, etc... de un conjunto de valoresgroup_by()
: permite agrupar filas en función de una o varias condicionesdplyr 1.0.0
, en mayo de 2020, dos funciones más:across()
y where()
. Estas funciones son un poco diferentes, solo se usan en combinación de otra función/verbo. Son 2 funciones que en la jerga del tidyverse no son verbos sino adverbios. Lo vemosgapminder <- gapminder::gapminder #- cargamos los datos
gapminder <- gapminder::gapminder #- cargamos los datos
Por supuesto: hace accesibles, carga en memoria de R, los datos de gapminder.
El conjunto de datos gapminder está en el pkg gapminder
filter()
: permite seleccionar filas
Filas que cumplan una determinadas condiciones o criterios lógicos. Por ejemplo:
gapminder <- gapminder::gapminder #- cargamos los datos#- Observaciones de España (country == "Spain")aa <- gapminder %>% filter(country == "Spain") #- filas con valores de "lifeExp" < 29aa <- gapminder %>% filter(lifeExp < 29) #- filas con valores de "lifeExp" entre [29, 32]aa <- gapminder %>% filter(lifeExp >= 29 , lifeExp <= 32) aa <- gapminder %>% filter(lifeExp >= 29 & lifeExp <= 32) aa <- gapminder %>% filter(between(lifeExp, 29, 32)) #- observaciones de países de África con lifeExp > 32aa <- gapminder %>% filter(lifeExp > 72 & continent == "Africa") #- observaciones de países de África o Asia con lifeExp > 32aa <- gapminder %>% filter(lifeExp > 72 & continent %in% c("Africa", "Asia") ) aa <- gapminder %>% filter(lifeExp > 72 & (continent == "Africa" | continent == "Asia") )
slice()
: permite seleccionar filas pero por posición.filter()
y slice()
ambas seleccionan filas, la primera por condiciones y la segunda por posición:#- selecciona las observaciones de la décima a la quinceavaaa <- gapminder %>% slice(c(10:15)) #- selecciona las observaciones de la 12 a 14 Y de la 44 a 46, Y las 4 últimasaa <- gapminder %>% slice( c(12:14, 44:46, n()-4:n()) ) #- AQUI hay un error, tenéis que arreglarlo. #- Pista: igual os ayuda crear una columna con el índice de rows y repetir el cálculoaa <- gapminder %>% mutate(index = 1:n())aa <- gapminder %>% slice( c(12:14, 44:46, n()-4:n()) )
slice()
slice_max()
y slice_min()
: seleccionan filas con valor máximo (o mínimo) de una variable: #- selecciona las 3 filas con mayor valor de lifeExpaa <- gapminder %>% slice_max(lifeExp, n = 3)#- selecciona las 4 filas con MENOR valor de popaa <- gapminder %>% slice_min(pop, n = 4)#- observaciones en el primer decil en cuanto a esperanza de vida, 10% con menor esperanza de vidaaa <- gapminder %>% slice_min(lifeExp, prop = 0.1)#- 1% de observaciones con mayor población. Imagino que estarán China e Indiaaa <- gapminder %>% slice_max(pop, prop = 0.01)
slice()
slice_max()
y slice_min()
: seleccionan filas con valor máximo (o mínimo) de una variable: #- selecciona las 3 filas con mayor valor de lifeExpaa <- gapminder %>% slice_max(lifeExp, n = 3)#- selecciona las 4 filas con MENOR valor de popaa <- gapminder %>% slice_min(pop, n = 4)#- observaciones en el primer decil en cuanto a esperanza de vida, 10% con menor esperanza de vidaaa <- gapminder %>% slice_min(lifeExp, prop = 0.1)#- 1% de observaciones con mayor población. Imagino que estarán China e Indiaaa <- gapminder %>% slice_max(pop, prop = 0.01)
A veces se necesita obtener una muestra aleatoria de los datos: por ejemplo con slice_sample()
:
#- selecciona (aleatoriamente) 100 filas de los datosaa <- gapminder %>% slice_sample(n = 100)#- selecciona (aleatoriamente) un 5% de los datosaa <- gapminder %>% slice_sample(prop = 0.05)
arrange()
: permite reordenar las filas de un df#- ordena las filas de MENOR a mayor según los valores de la v. lifeExp aa <- gapminder %>% arrange(lifeExp)#- ordena las filas de MAYOR a menor según los valores de la v. lifeExpaa <- gapminder %>% arrange(desc(lifeExp)) #- ordena las filas de MENOR a mayor según los valores de la v. lifeExp. #- Si hay empates se resuelve con la variable "pop"aa <- gapminder %>% arrange(lifeExp, pop)
rename()
: permite cambiar los nombres de las variables#- cambia los nombres de lifeExp y gdpPercap a life_exp y gdp_percap gapminder %>% rename(life_exp = lifeExp, gdp_percap = gdpPercap)
names()
es útil#-(!!) la función names() de R-base es muy útil. aa <- gapmindernames(aa) <- names(aa) %>% touppernames(aa) <- names(aa) %>% tolower
rename_with()
permite hacer transformaciones más complejas [🌶]aa <- gapminderaa %>% rename_with(toupper)rename_with(aa, toupper, starts_with("Life") | contains("countr"))rename_with(aa, ~ str_replace(.x, "e", "Ö")) #- (!!!!)
select()
se utiliza para seleccionar variablesaa <- gapminder %>% select(year, lifeExp)
aa <- gapminder %>% select(1:3, 5)
select()
se utiliza para seleccionar variablesaa <- gapminder %>% select(year, lifeExp)
aa <- gapminder %>% select(1:3, 5)
aa <- gapminder %>% select(-year)#- Para eliminar varias variablesaa <- gapminder %>% select(-c(year, lifeExp))
aa <- gapminder %>% select(-c(1:3, 5))
select()
junto a la función where()
select()
y where()
son dos funciones, sí, pero en la jerga del tidyverse, select()
es un verbo y where()
es un adverbio, cualifica/cambia lo que hace select()
.select()
junto a la función where()
select()
y where()
son dos funciones, sí, pero en la jerga del tidyverse, select()
es un verbo y where()
es un adverbio, cualifica/cambia lo que hace select()
.En gapminder
las 2 primeras variables (country y continent) son factores y las 4 siguientes son variable numéricas.
Imagina que queremos seleccionar sólo las variables que son numéricas. Podemos hacerlo por nombre o por posición pero mejor con select()
y la función auxiliar where()
aa <- gapminder %>% select(is.numeric) #- funciona, pero ...aa <- gapminder %>% select(where(is.numeric)) #- es "preferible" esta segunda expresión
select()
junto a la función where()
select()
y where()
son dos funciones, sí, pero en la jerga del tidyverse, select()
es un verbo y where()
es un adverbio, cualifica/cambia lo que hace select()
.En gapminder
las 2 primeras variables (country y continent) son factores y las 4 siguientes son variable numéricas.
Imagina que queremos seleccionar sólo las variables que son numéricas. Podemos hacerlo por nombre o por posición pero mejor con select()
y la función auxiliar where()
aa <- gapminder %>% select(is.numeric) #- funciona, pero ...aa <- gapminder %>% select(where(is.numeric)) #- es "preferible" esta segunda expresión
aa <- gapminder %>% select(!where(is.numeric))
select()
para renombrar y reordenar las variables#- dejamos en aa solamente a las columnas "year" y "pop"; ADEMÁS, ahora, "pop" irá antes que "year"aa <- gapminder %>% select(pop, year)#- dejamos en aa solamente a las columnas "year" y "pop" y les cambiamos el nombreaa <- gapminder %>% select(poblacion = pop, año = year)
Imagina que quieres que la última columna pase a ser la primera (manías!!). Podemos hacerlo con select y everything()
. everything es una función auxiliar:
#- "gdpPercap" que es la última columna pasa a ser la primeraaa <- gapminder %>% select(gdpPercap, everything())
select()
para renombrar y reordenar las variables#- dejamos en aa solamente a las columnas "year" y "pop"; ADEMÁS, ahora, "pop" irá antes que "year"aa <- gapminder %>% select(pop, year)#- dejamos en aa solamente a las columnas "year" y "pop" y les cambiamos el nombreaa <- gapminder %>% select(poblacion = pop, año = year)
Imagina que quieres que la última columna pase a ser la primera (manías!!). Podemos hacerlo con select y everything()
. everything es una función auxiliar:
#- "gdpPercap" que es la última columna pasa a ser la primeraaa <- gapminder %>% select(gdpPercap, everything())
relocate()
otra función para reordenar las variables de un dfaa <- gapminder %>% dplyr::relocate(country, .after = lifeExp)aa <- gapminder %>% dplyr::relocate(country, .before = lifeExp)
mutate()
para crear nuevas variablesaa <- gapminder %>% mutate(GDP = pop*gdpPercap)
Por defecto, la nueva variable creada se situará al final del df, a no ser que usemos los argumentos .after
y .before
[🌶]
aa <- gapminder %>% mutate(GDP = pop*gdpPercap, .after = country)aa <- gapminder %>% mutate(GDP = pop*gdpPercap, .before = country)
summarize()
para "resumir" variablesCoge una variable como input y devuelve un solo valor; por ejemplo, haya la media aritmética (o el mínimo, o el máximo ...) de una columna/variable.
Empezamos "resumiendo" una sola variable:
aa <- gapminder %>% summarise(media = mean(lifeExp)) aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) aa <- gapminder %>% summarise(max(pop)) aa <- gapminder %>% summarise(NN = n()) aa <- gapminder %>% count() #- más adelante veremos la utilidad de count()
summarize()
para "resumir" variablesCoge una variable como input y devuelve un solo valor; por ejemplo, haya la media aritmética (o el mínimo, o el máximo ...) de una columna/variable.
Empezamos "resumiendo" una sola variable:
aa <- gapminder %>% summarise(media = mean(lifeExp)) aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) aa <- gapminder %>% summarise(max(pop)) aa <- gapminder %>% summarise(NN = n()) aa <- gapminder %>% count() #- más adelante veremos la utilidad de count()
#- retornará 2 valores: las medias de "lifeExp" y "gdpPercap"aa <- gapminder %>% summarise(mean(lifeExp), mean(gdpPercap))
summarize()
para "resumir" variablesCoge una variable como input y devuelve un solo valor; por ejemplo, haya la media aritmética (o el mínimo, o el máximo ...) de una columna/variable.
Empezamos "resumiendo" una sola variable:
aa <- gapminder %>% summarise(media = mean(lifeExp)) aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) aa <- gapminder %>% summarise(max(pop)) aa <- gapminder %>% summarise(NN = n()) aa <- gapminder %>% count() #- más adelante veremos la utilidad de count()
#- retornará 2 valores: las medias de "lifeExp" y "gdpPercap"aa <- gapminder %>% summarise(mean(lifeExp), mean(gdpPercap))
#- retornará 2 valores: la media y sd de la v. "lifeExp"aa <- gapminder %>% summarise(mean(lifeExp), sd(lifeExp))
summarize()
con across()
Usar across()
permitirá calcular estadísticos de todas las variables, o de subconjuntos de estas, de manera más cómoda:
#- media de cada una de las 6 variables. Devuelve 2 warnings porque las 2 primeras son textuales. No se puede calcular la media de continent y countrygapminder %>% summarise(across(everything(), mean) ) #- calculamos la media de tercera a la sexta variablegapminder %>% summarise(across(3:6, mean) )
across()
permite seleccionar variables para hacer summaries. summarize()
con across()
Usar across()
permitirá calcular estadísticos de todas las variables, o de subconjuntos de estas, de manera más cómoda:
#- media de cada una de las 6 variables. Devuelve 2 warnings porque las 2 primeras son textuales. No se puede calcular la media de continent y countrygapminder %>% summarise(across(everything(), mean) ) #- calculamos la media de tercera a la sexta variablegapminder %>% summarise(across(3:6, mean) )
across()
permite seleccionar variables para hacer summaries. across()
se puede utilizar where()
para aplicar criterios lógicos para seleccionar variables: [🌶] [ 🌟 ]gapminder %>% summarise(across(where(is.numeric), mean)) #- con los nombres de los argumentos (más largo pero conviene verlo de vez en cuando)gapminder %>% summarise(across(.cols = where(is.numeric), .fns = mean))
summarize()
con across()
y varias funciones(list)summarise()
con across()
pero, además, tendrás que poner la lista de funciones dentro de list()
. [🌶] [ 🌟 ] #- calculamos la media y desviación típica de las columnas 3 a 6.gapminder %>% summarise(across(3:6, list(media = mean, desv = sd)))
#- lo mismo, pero explicitando los nombres de los argumentos [🌶] gapminder %>% summarise(across(.cols = 3:6, .fns = list(media = mean, desv = sd) ))#- lo mismo otra vez, pero eligiendo el nombre de las variables que se van a crear con .names [🌶] [🌶] gapminder %>% summarise(across(3:6, list(media = mean, desv = sd), .names = "{fn}_{col}"))
group_by()
. Con está función ya se puede ver la potencia de dplyr
En análisis de datos muchas operaciones queremos calcularlas para distintos grupos (p. ej. mujer/hombre, ...). group_by()
permite hacerlo.
group_by()
coge un df y lo convierte en un "df agrupado". En ese nuevo "df agrupado", las operaciones que hagamos, se harán por separado para cada uno de los grupos que hayamos definido. Ahora lo vemos.
group_by()
. Con está función ya se puede ver la potencia de dplyr
En análisis de datos muchas operaciones queremos calcularlas para distintos grupos (p. ej. mujer/hombre, ...). group_by()
permite hacerlo.
group_by()
coge un df y lo convierte en un "df agrupado". En ese nuevo "df agrupado", las operaciones que hagamos, se harán por separado para cada uno de los grupos que hayamos definido. Ahora lo vemos.
#- cogemos df y lo (des)agrupamos por grupos #- definidos por la variable "continent"; o sea, habrá 5 grupos#- después con summarise() calcularemos el nº de observaciones en cada grupo;#- es decir, nos retornará un df con una fila por cada continenteaa <- gapminder %>% group_by(continent) %>% summarise(NN = n()) aa#> # A tibble: 5 × 2#> continent NN#> <fct> <int>#> 1 Africa 624#> 2 Americas 300#> 3 Asia 396#> 4 Europe 360#> 5 Oceania 24
group_by()
Pregunta: ¿cuantos países hay en la base de datos?
aa <- gapminder %>% group_by(country) %>% summarize(NN = n())
group_by()
Pregunta: ¿cuantos países hay en la base de datos?
aa <- gapminder %>% group_by(country) %>% summarize(NN = n())
#- cogemos df y lo agrupamos por "continent", #- después calculamos 2 cosas: el número de observaciones o rows#- y el número de países en cada continente (NN_countries)aa <- gapminder %>% group_by(continent) %>% summarize(NN = n(), NN_countries = n_distinct(country)) aa#> # A tibble: 5 × 3#> continent NN NN_countries#> <fct> <int> <int>#> 1 Africa 624 52#> 2 Americas 300 25#> 3 Asia 396 33#> 4 Europe 360 30#> 5 Oceania 24 2
group_by()
aa <- gapminder %>% group_by(continent) %>% summarize(NN = n(), NN_countries = length(unique(country)) )aa#> # A tibble: 5 × 3#> continent NN NN_countries#> <fct> <int> <int>#> 1 Africa 624 52#> 2 Americas 300 25#> 3 Asia 396 33#> 4 Europe 360 30#> 5 Oceania 24 2
dplyr
Primero algo sencillo para calentar: hay que calcular la esperanza de vida media por continente.
#- cogemos df y lo agrupamos por "continent", #- después calculamos la media de "lifeExp"gapminder %>% group_by(continent) %>% summarize(mean(lifeExp)) #> # A tibble: 5 × 2#> continent `mean(lifeExp)`#> <fct> <dbl>#> 1 Africa 48.9#> 2 Americas 64.7#> 3 Asia 60.1#> 4 Europe 71.9#> 5 Oceania 74.3
Calculemos la esperanza de vida media por continente en el primer periodo (1952)
#- cogemos df y filtramos para quedarnos con las observaciones de 1952#- después lo agrupamos por "continent", #- después calculamos la media de "lifeExp"gapminder %>% filter(year == "1952") %>% group_by(continent) %>% summarize(mean(lifeExp))
¿Qué hace este trozo de código?
gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(mean(lifeExp), mean(gdpPercap))
#- cogemos df y filtramos las observaciones de 1952 y 2007#- agrupamos por "continent", #- después calculamos la media de "lifeExp" y de "gdpPercap"gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(mean(lifeExp), mean(gdpPercap)) #> # A tibble: 10 × 4#> # Groups: continent [5]#> continent year `mean(lifeExp)` `mean(gdpPercap)`#> <fct> <int> <dbl> <dbl>#> 1 Africa 1952 39.1 1253.#> 2 Africa 2007 54.8 3089.#> 3 Americas 1952 53.3 4079.#> 4 Americas 2007 73.6 11003.#> 5 Asia 1952 46.3 5195.#> 6 Asia 2007 70.7 12473.#> 7 Europe 1952 64.4 5661.#> 8 Europe 2007 77.6 25054.#> 9 Oceania 1952 69.3 10298.#> 10 Oceania 2007 80.7 29810.
across()
#- Voy a crear un nuevo df: "gapminder_gr" o "gapminder agrupado"gapminder_gr <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) #- y sobre "gapminder_gr" iremos haciendo cálculos#- si queremos calcular la media de varias variables tenemos que usar across()gapminder_gr %>% summarise(across(c(lifeExp, gdpPercap), mean))#- si queremos calcular la media de todas las variables numéricas tenemos que usar across() y where()gapminder_gr %>% summarise(across(where(is.numeric), mean))#- si queremos calcular la media y la mediana, hay que usar list()gapminder_gr %>% summarise(across(c(lifeExp, gdpPercap), list (media = mean, mediana = median) ))#- si ponemos los nombres de los argumentos quedaría comogapminder_gr %>% summarise(across(.cols = c(lifeExp, gdpPercap), .fns = list (media = mean, mediana = median)))#- además, podemos controlar el nombre de las variables creadas con el argumento .namesgapminder_gr %>% summarise(across(c(lifeExp, gdpPercap), list (media = mean, mediana = median), .names = "{fn}_{col}"))
gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup()#> # A tibble: 10 × 3#> continent year media#> <fct> <int> <dbl>#> 1 Africa 1952 39.1#> 2 Africa 2007 54.8#> 3 Americas 1952 53.3#> 4 Americas 2007 73.6#> 5 Asia 1952 46.3#> 6 Asia 2007 70.7#> 7 Europe 1952 64.4#> 8 Europe 2007 77.6#> 9 Oceania 1952 69.3#> 10 Oceania 2007 80.7
#- se puede hacer de una vez, pero vamos a partir el código en 2 trozosaa <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup()aa1 <- aa %>% group_by(continent) %>% summarise(min_l = min(media), max_l = max(media)) %>% mutate(dif = max_l-min_l) %>% arrange(desc(dif))aa1#> # A tibble: 5 × 4#> continent min_l max_l dif#> <fct> <dbl> <dbl> <dbl>#> 1 Asia 46.3 70.7 24.4#> 2 Americas 53.3 73.6 20.3#> 3 Africa 39.1 54.8 15.7#> 4 Europe 64.4 77.6 13.2#> 5 Oceania 69.3 80.7 11.5
#- segundo intento: se puede hacer de una vez, pero vamos a partir el código en 2 trozosaa <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup() #- usamos lag()aa1 <- aa %>% group_by(continent) %>% arrange(year) %>% mutate(variac_l = media - lag(media))#- mostramos los resultadosaa1 %>% filter(year == 2007) %>% arrange(desc(variac_l))#> # A tibble: 5 × 4#> # Groups: continent [5]#> continent year media variac_l#> <fct> <int> <dbl> <dbl>#> 1 Asia 2007 70.7 24.4#> 2 Americas 2007 73.6 20.3#> 3 Africa 2007 54.8 15.7#> 4 Europe 2007 77.6 13.2#> 5 Oceania 2007 80.7 11.5
#- esta parte es comúnaa <- gapminder %>% filter(year %in% c(1952, 2007)) %>% group_by(continent, year) %>% summarize(media = mean(lifeExp)) %>% ungroup()#- pero ahora usamos pivot_wider()aa %>% pivot_wider(names_from = year, values_from = media) %>% mutate(dif_l = 2007 - 1952) %>% arrange(desc(dif_l))#> # A tibble: 5 × 4#> continent `1952` `2007` dif_l#> <fct> <dbl> <dbl> <dbl>#> 1 Africa 39.1 54.8 55#> 2 Americas 53.3 73.6 55#> 3 Asia 46.3 70.7 55#> 4 Europe 64.4 77.6 55#> 5 Oceania 69.3 80.7 55
aa <- gapminder %>% group_by(continent, year) %>% select(continent, year, lifeExp) %>% summarise(mean_life = mean(lifeExp)) %>% arrange(year) %>% mutate(incre_mean_life_0 = mean_life - first(mean_life)) %>% mutate(incre_mean_life_t = mean_life - lag(mean_life)) %>% arrange(continent)#- por ejemplo veamos el resultado para Europeaa %>% filter(continent == "Europe")
En palabras de Jennyfer Bryan:
Break the code into pieces, starting at the top, and inspect the intermediate results. That’s certainly how I was able to write such a thing. These commands do not leap fully formed out of anyone’s forehead – they are built up gradually, with lots of errors and refinements along the way. Is the statement above really hard for you to read? If yes, then by all means break it into pieces and make some intermediate objects. Your code should be easy to write and read when you’re done.
#- variación de lifeExp en Spain año a año (bueno lustro a lustro)gapminder %>% group_by(country) %>% select(country, year, lifeExp) %>% mutate(lifeExp_gain_cada_lustro = lifeExp - lag(lifeExp)) %>% filter(country == "Spain" )#> # A tibble: 12 × 4#> # Groups: country [1]#> country year lifeExp lifeExp_gain_cada_lustro#> <fct> <int> <dbl> <dbl>#> 1 Spain 1952 64.9 NA #> 2 Spain 1957 66.7 1.72 #> 3 Spain 1962 69.7 3.03 #> 4 Spain 1967 71.4 1.75 #> 5 Spain 1972 73.1 1.62 #> 6 Spain 1977 74.4 1.33 #> 7 Spain 1982 76.3 1.91 #> 8 Spain 1987 76.9 0.600#> 9 Spain 1992 77.6 0.670#> 10 Spain 1997 78.8 1.20 #> 11 Spain 2002 79.8 1.01 #> 12 Spain 2007 80.9 1.16
¿Y la variación acumulada? Fácil!! Sólo tendríamos que sumar o acumular la variable "lifeExp_gain_cada_lustro" que hemos generado anteriormente, así que sólo habría que añadir una linea a nuestro código:
gapminder %>% group_by(country) %>% select(country, year, lifeExp) %>% mutate(lifeExp_gain_cada_lustro = lifeExp - lag(lifeExp)) %>% #- Al final para hacerlo (como había pensado) me han hecho falta 2 lineas, #- porque la primera observación de "lifeExp_gain_cada_lustro" es un NA #- y eso hacía que la función cumsum() no funcionase.mutate(lifeExp_gain_cada_lustro2 = `ifelse`(is.na(lifeExp_gain_cada_lustro), 0, lifeExp_gain_cada_lustro)) %>% mutate(lifeExp_gain_acumulado = cumsum(lifeExp_gain_cada_lustro2)) %>% filter(country == "Spain")
Otra solución, además es más fácil:
#- ganancia acumulada (otra forma de hacer lo mismo)gapminder %>% group_by(country) %>% select(country, year, lifeExp) %>% mutate(lifeExp_gain_acumulada = lifeExp - lifeExp[1]) %>% filter(country == "Spain")#> # A tibble: 12 × 4#> # Groups: country [1]#> country year lifeExp lifeExp_gain_acumulada#> <fct> <int> <dbl> <dbl>#> 1 Spain 1952 64.9 0 #> 2 Spain 1957 66.7 1.72#> 3 Spain 1962 69.7 4.75#> 4 Spain 1967 71.4 6.5 #> 5 Spain 1972 73.1 8.12#> 6 Spain 1977 74.4 9.45#> 7 Spain 1982 76.3 11.4 #> 8 Spain 1987 76.9 12.0 #> 9 Spain 1992 77.6 12.6 #> 10 Spain 1997 78.8 13.8 #> 11 Spain 2002 79.8 14.8 #> 12 Spain 2007 80.9 16.0
aa <- gapminder %>% filter(continent == "Asia") %>% select(year, country, lifeExp) %>% group_by(year) %>% slice_max(n = 3, lifeExp) %>% arrange(year)
Una función auxiliar que es muy útil al utilizarla junto a mutate: case_when()
.
aa <- gapminder %>% group_by(continent, year) %>% mutate(media_lifeExp = mean(lifeExp)) %>% mutate(media_gdpPercap = mean(gdpPercap)) %>% mutate(GOOD_or_BAD = case_when( lifeExp > mean(lifeExp) & gdpPercap > mean(gdpPercap) ~ "good", lifeExp < mean(lifeExp) & gdpPercap < mean(gdpPercap) ~ "bad" , lifeExp < mean(lifeExp) | gdpPercap < mean(gdpPercap) ~ "medium" )) %>% filter(country == "Spain")
bind_cols()
y bind_rows()
bind_cols()
(o con cbind() de R-base)df_1 <- iris[ , 1:2] ; df_2 <- iris[ , 3:5]df_1 <- iris %>% select(1:2) ; df_2 <- iris %>% select(3:5) df_3 <- bind_cols(df_1, df_2)identical(iris, df_3)
bind_cols()
y bind_rows()
bind_cols()
(o con cbind() de R-base)df_1 <- iris[ , 1:2] ; df_2 <- iris[ , 3:5]df_1 <- iris %>% select(1:2) ; df_2 <- iris %>% select(3:5) df_3 <- bind_cols(df_1, df_2)identical(iris, df_3)
bind_rows()
(o con rbind() de R-base)df_1 <- iris[1:75, ] ; df_2 <- iris[76:150, ]df_1 <- iris %>% slice(1:75) ; df_2 <- iris %>% slice(76:150) df_3 <- bind_rows(df_1, df_2)identical(iris, df_3)
En dplyr hay 3 tipos de funciones(verbos) que se ocupan de diferentes operaciones para unir datasets:
Mutating joins, añade nuevas variables (o columnas) a un dataframe (df1). Estas nuevas columnas vienen de un segundo df2 (hay varias mutating joins, dependiendo del criterio para seleccionar las filas)
Filtering joins, filtra las filas (observaciones) de un dataframe (df1) basándose en si las filas de df1 coinciden (match) o no con una observación del segundo df2
Set operations, combina las observaciones de los dos datasets (df1 y df2) as if they were set elements.
Hay 3/4 tipos de mutating joins. Su sintaxis es idéntica, sólo se diferencian en que las filas que se seleccionan dependen del criterio para hacer el match:
inner_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2, PERO solo retorna las filas de df1 que tienen una equivalencia en df2. (la equivalencia se define en función del valor de una variable o variables comunes en df1 y df2)left_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1. (Si hubiesen varios matches entre df1 e df2 se retornan todas las combinaciones!!!!) full_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1 y de df2. Osea, retorna TODAS las filas y TODAS las columnas de las 2 tablas. (Donde no hay matches retorna NA's)Hay 3/4 tipos de mutating joins. Su sintaxis es idéntica, sólo se diferencian en que las filas que se seleccionan dependen del criterio para hacer el match:
inner_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2, PERO solo retorna las filas de df1 que tienen una equivalencia en df2. (la equivalencia se define en función del valor de una variable o variables comunes en df1 y df2)left_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1. (Si hubiesen varios matches entre df1 e df2 se retornan todas las combinaciones!!!!) full_join(df1,df2)
: Retorna todas las columnas de df1 y también las de df2; en cuanto a las filas, retorna TODAS las filas de df1 y de df2. Osea, retorna TODAS las filas y TODAS las columnas de las 2 tablas. (Donde no hay matches retorna NA's)x <- tibble(id = 1:3, x = paste0("x", 1:3))y <- tibble(id = (1:4)[-3], y = paste0("y", (1:4)[-3]))
#- only includes observations that match in both x and ydf_inner <- inner_join(x, y)
#- full_join() includes all observations from x and ydf_full_join <- full_join(x, y)
#- includes all observations in x, regardless of whether they match or not. #- This is the most commonly used join because it ensures that you don’t lose observations from your primary table.df_left_join <- left_join(x, y)