Programación y manejo de datos con R
(Web del curso aquí)
dplyrEn el tema anterior (05A) vimos:
%>% y |>), para encadenar funcionestidyr (pivot_longer(), pivot_wider(), separate(), unite(), …)dplyrdplyr es el paquete más importante a la hora de manipular datos.dplyr
dplyr es un paquete que permite manipular datos de forma intuitivaTiene 6-7 funciones (o verbos) principales
%>%)sintaxis de dplyr
Todas las funciones tienen una estructura o comportamiento similar:
el primer argumento siempre es un df
los siguientes argumentos describen qué hacer con los datos.
el resultado es siempre un nuevo df
principales funciones de dplyr
select() : selecciona columnasfilter() : filtra/selecciona filas (que cumplen una o varias condiciones)mutate() : crea nuevas columnas (variables)arrange(): reordena las filasrename() : cambia los nombres de las columnassummarise() : resume (colapsa) los valores de una columna a un solo valor. Por ejemplo, calcula la media, máximo, mínimo, etc…hay una séptima función (que da mucha potencia a dplyr)
group_by() : “agrupa” filas (en función de una o varias condiciones)across() y where()dplyr con ejemplostrabajaremos con datos del pkg gapminder
¿Supongo que ya sabéis qué hace el siguiente código?
gapminder que está en el paquete gapminderselect() se utiliza para seleccionar variables
A veces queremos reordenar las columnas/variables
relocate()A veces queremos renombrar las columnas
rename()la función names() de R-base es útil
A veces queremos CREAR nuevas columnas
Podemos hacerlo con mutate()
Por ejemplo, podemos crear la variable: GDP = pop * gdpPercap
filter(): permite seleccionar filas
filas que cumplan unas determinadas condiciones o criterios lógicos
gapminder <- gapminder::gapminder #- cargamos los datos
#- Observaciones de España (country == "Spain")
aa <- gapminder %>% filter(country == "Spain")
#- filas con valores de "lifeExp" < 29
aa <- gapminder %>% filter(lifeExp < 29)
#- filas con valores de "lifeExp" entre [29, 32]
aa <- gapminder %>% filter(lifeExp >= 29 & lifeExp <= 32)
aa <- gapminder %>% filter(between(lifeExp, 29, 32))
#- observaciones de países de África con lifeExp > 72
aa <- gapminder %>% filter(lifeExp > 72 & continent == "Africa")
#- observaciones de países de África o Asia con lifeExp > 72
aa <- gapminder %>% filter(lifeExp > 72 & continent %in% c("Africa", "Asia") )
aa <- gapminder %>% filter(lifeExp > 72 & (continent == "Africa" | continent == "Asia") ) slice() es una variante de filter()
permite seleccionar filas pero por posición
filter() y slice() ambas seleccionan filas, la primera por condiciones y la segunda por posición:variantes de 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 lifeExp
aa <- gapminder %>% slice_max(lifeExp, n = 3)
#- selecciona las 4 filas con MENOR valor de pop
aa <- gapminder %>% slice_min(pop, n = 4)
#- observaciones en el primer decil en cuanto a esperanza de vida, 10% con menor esperanza de vida
aa <- gapminder %>% slice_min(lifeExp, prop = 0.1)
#- 1% de observaciones con mayor población. Imagino que estarán China e India
aa <- gapminder %>% slice_max(pop, prop = 0.01)variantes de slice()
slice_sample(): permite obtener una muestra aleatoria de los datosarrange(): 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. lifeExp
aa <- gapminder %>% arrange(desc(lifeExp))
#- ordena las filas de MENOR a mayor según los valores de la v. lifeExp.
#- Si hubiese empates se resuelve con la variable "pop"
aa <- gapminder %>% arrange(lifeExp, pop) summarise() para “resumir” variables
summarise(): coge una variable como input y devuelve un solo valor; por ejemplo, calcula la media aritmética (o el mínimo, o el máximo …) de una columna/variableaa <- gapminder %>% summarise(maximo = max(pop)) #- el valor máximo de la variable "pop"
aa <- gapminder %>% summarise(NN = n()) #- el número de observaciones
aa <- gapminder %>% summarise(media = mean(lifeExp)) #- la media de la variable "lifeExp"
aa <- gapminder %>% summarise(desviacion_tipica = sd(lifeExp)) #- os lo explicarán en Estadística summarise(): “resumimos” dos variables
group_by(): con esta 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, diferentes países, provincias …). 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.# ahora queremos calcular el nº de observaciones de cada continente
# cogemos el df y lo agrupamos por grupos definidos por la variable "continent"
# o sea, habrá 5 grupos (5 continentes)
# después con summarise() calcularemos el nº de observaciones en cada grupo;
# es decir, nos retornará un df con una fila por cada continente
# con el nº de observaciones de cada continente
bb <- gapminder %>% group_by(continent) %>% summarise(NN = n())Tarea: practicando los verbos de dplyr
gapminder, obtén los países de Europa que en 2007 tenían una esperanza de vida mayor de 79 añoscountry y lifeExp, y ordénalos de mayor a menor esperanza de vidafilter(), select() y arrange()filter() puedes poner varias condiciones separadas por & (o por comas)desc()con dplyr 1.0.0, en 2020, aparecieron dos funciones importantes: across() y where()
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, cualifican/cambian lo que hacen otras funciones como select()Ejemplo de uso: select() junto a la función where() [🌶🌶🌶🌶]
gapminder las 2 primeras variables (country y continent) son factores y las 4 siguientes son variables numéricas.select() y where()más ejemplos de across() y where()
gapminder. Tendríamos que repetir código 6 veces … o usar across()across() y where() con summarise() 😱
Dentro de across() se puede utilizar where()
Con esto conseguimos poder aplicar criterios lógicos en la selección de variables: [🌶] [ 🌟 ]
Si quisiéramos calcular la media de las variables numéricas
Ahora no os asustéis de lo que salga: es lo mismo que lo de arriba pero con los nombres de los argumentos
summarise() con across() y varias funciones 😱😱😱😱
Si quieres calcular varios estadísticos de varias variables; por ejemplo, la media y la desviación típica de un grupo de variables
Tendrás que seguir utilizando summarise() con across() pero, además, tendrás que poner la lista de funciones dentro de list(). [🌶] [ 🌟 ]
Si quisiéramos calcular la media y la desviación típica de las variables 3 a 6
Otra vez, no os asustéis de lo que salga: es lo mismo que lo de arriba pero con los nombres de los argumentos
Ya lo último: añadimos un argumento más .names, que nos permite controlar el nombre de las nuevas variables
Más ejemplos para afianzar el uso de across()
Creamos un nuevo df: “gapminder_gr” o “gapminder agrupado”
Hacemos cálculos partiendo de “gapminder_gr”. Recuerda que está “agrupado” por continente y año
#- 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 como
gapminder_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 .names
gapminder_gr %>% summarise(across(c(lifeExp, gdpPercap),
list (media = mean, mediana = median),
.names = "{fn}_{col}"))Extensión: agrupar “solo para una operación” con .by [🌶🌶]
summarise() devuelve un df que sigue agrupado (por la primera variable). Por eso hacemos tantos ungroup()dplyr 1.1.0 (2023) se puede usar el argumento .by: agrupa solo para esa operación y el resultado ya sale desagrupadogroup_by(), que es lo que verás en la mayoría del código, pero conviene conocer .bydplyrSeguimos con más ejemplos para afianzar nuestros conocimientos de los verbos de dplyr: select(), filter(), mutate(), arrange(), rename(), summarise() y group_by()
Empezamos con ejemplos sencillos; pronto llegaremos a “preguntas de verdad”
para aprender a manipular datos con dplyr
Es importante saber cómo contar las observaciones que tenemos en distintos grupos
Por ejemplo, contar cuántas mujeres hay, o cuántas observaciones hay en cada continente, o cuántas empresas en diferentes sectores, …
… vamos a CONTAR
tenemos 3 formas distintas de contar el nº de observaciones (o filas)
1. Usando mutate() con n()
2. Usando summarise() con n()
… seguimos CONTANDO
Pero ahora contamos el nº de observaciones de distintos grupos (por ejemplo el nº de observaciones de cada continente)
mutate() pero esta vez solo usaremos summarise() y count()… aún seguimos CONTANDO
Contamos el nº de observaciones de distintos grupos, pero ahora definidos por dos variables (por ejemplo el nº de observaciones de cada continente y año)
Extensión: n() versus nrow()
n() es una función auxiliar en el tidyverse. Devuelve el nº de filas “in the current group”. Solo funciona en el tidyverse, concretamente en funciones como mutate() y summarise()nrow() es una función de R-base. Devuelve el número de filas en xnrow(.) sale 1704 en todos los grupos: nrow(.) cuenta todas las filas porque es una función antigua y no sabe que hacer con un df agrupado. Por eso, dentro de summarise() o mutate(), para contar las filas de cada grupo hay que usar n()… ver/obtener observaciones DISTINTAS con distinct()
… CONTAR observaciones DISTINTAS con n_distinct()
calculando ESTADÍSTICOS: máximo, mínimo, media, ……
calcular ESTADÍSTICOS (máximo, mínimo y media) por continente
ESTADÍSTICOS para Europa año a año ……
#- calcular estadísticos por CONTINENTE Y AÑO
aa <- gapminder %>%
1 group_by(continent, year) %>%
2 summarise(maximo = max(lifeExp, na.rm = TRUE),
minimo = min(lifeExp, na.rm = TRUE),
mean = mean(lifeExp, na.rm = TRUE) ) %>%
3 ungroup() %>%
4 filter(continent == "Europe")group_by() es una buena práctica hacer un ungroup()
| continent | year | maximo | minimo | mean |
|---|---|---|---|---|
| Europe | 1952 | 72.67 | 43.59 | 64.41 |
| Europe | 1957 | 73.47 | 48.08 | 66.70 |
| Europe | 1962 | 73.68 | 52.10 | 68.54 |
| Europe | 1967 | 74.16 | 54.34 | 69.74 |
| Europe | 1972 | 74.72 | 57.01 | 70.78 |
| Europe | 1977 | 76.11 | 59.51 | 71.94 |
| Europe | 1982 | 76.99 | 61.04 | 72.81 |
| Europe | 1987 | 77.41 | 63.11 | 73.64 |
| Europe | 1992 | 78.77 | 66.15 | 74.44 |
| Europe | 1997 | 79.39 | 68.83 | 75.51 |
| Europe | 2002 | 80.62 | 70.84 | 76.70 |
| Europe | 2007 | 81.76 | 71.78 | 77.65 |
en la slide anterior, al calcular la esperanza de vida media por continente,
estábamos promediando países con diferente población 🫣
Vuelve a calcular la esperanza de vida media por continente y año, PERO ahora, ten en cuenta que los países tienen distinto nº de habitantes
Es decir, hemos de calcular la media, pero ponderada por la población (pop)
Pista: Igual puedes reusar el código de la página anterior, pero en lugar de usar mean() usar weighted.mean()
Tendrás que mirar la ayuda de weighted.mean() para usar un nuevo argumento
aa <- gapminder %>%
group_by(continent, year) %>%
1 summarise(mean = mean(lifeExp, na.rm = TRUE),
mean_w = weighted.mean(lifeExp, w = pop, na.rm = TRUE)) %>%
ungroup() %>%
2 filter(year == 2007)group_by() no es solo para summarise()
summarise() obtenemos una fila por grupo. Pero group_by() también se usa con mutate() y con filter(): se mantienen las filas y el cálculo se hace dentro de cada grupogroup_by() + mutate() nos permite calcular estadísticas de cada grupo y añadirlas como nuevas columnas al df original#- ¿cuánto se desvía cada país de la media de su continente? (en 2007)
aa <- gapminder %>%
filter(year == 2007) %>%
select(continent, country, lifeExp) %>%
group_by(continent) %>%
1 mutate(media_cont = mean(lifeExp)) %>%
mutate(dif = lifeExp - media_cont) %>%
ungroup()media_cont será la media de cada continente
group_by() + filter() nos permite filtrar filas dentro de cada grupo#- el país con mayor esperanza de vida de cada continente (en 2007)
bb <- gapminder %>%
filter(year == 2007) %>%
select(continent, country, lifeExp) %>%
group_by(continent) %>%
1 filter(lifeExp == max(lifeExp)) %>%
ungroup()max(lifeExp) es el máximo de cada continente: nos quedamos con una fila por grupo
Por favor, sed conscientes de que: “las cosas no salen a la primera”
En palabras de Jenny 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.
empezamos con una pregunta sencilla, pero …
gapminder?Algunas funciones del tidyverse que te podrían servir:
count()
summarise() junto a otras funciones
distinct()
n_distinct(): es un poco peculiar
n()
Lógicamente con R-base también se puede contar:
nrow()
length()
unique()
pull(), que extrae una columna como vector (lo mismo que hace $):Con esta pregunta “tan sencilla” quería:
mostrar algunas funciones (length(), unique() y nrow()) de R-base
mostraros que … cuando hay que obtener “una sola cosa” , y esta cosa no es complicada, entonces, la solución en R-base suele ser sencilla
tidyverse se hace casi “indispensable”otra pregunta
En gapminder ¿Cuántos países distintos hay en cada continente?
Después, calcula el porcentaje de países de cada continente sobre el total
#- solución à la R-base (seguro q se puede mejorar algo ...)
bb <- unique(gapminder[, c("country", "continent")])
cc <- aggregate(country ~ continent, data = bb, FUN = length)
# Renombrar la columna de conteo
colnames(cc)[colnames(cc) == "country"] <- "NN"
# Calcular el número total de países únicos en gapminder
NN_total <- sum(cc$NN)
cc$NN_total <- NN_total
# Calcular el porcentaje de países de cada continente sobre el total
cc$porcentaje <- (cc$NN / cc$NN_total) * 100ahora … una pregunta de verdad
#- se podría hacer de una vez, pero partimos el código en 2 trozos
aa <- gapminder %>% filter(year %in% c(1952, 2007)) %>%
group_by(continent, year) %>%
summarise(media = mean(lifeExp)) %>% ungroup()
bb <- aa %>% group_by(continent) %>%
summarise(min_l = min(media), max_l = max(media)) %>%
mutate(dif = max_l-min_l) %>%
arrange(desc(dif))En esta solución “podría haber un problema”
| continent | year | media |
|---|---|---|
| Africa | 1952 | 39.13550 |
| Africa | 2007 | 54.80604 |
| Americas | 1952 | 53.27984 |
| Americas | 2007 | 73.60812 |
| Asia | 1952 | 46.31439 |
| Asia | 2007 | 70.72848 |
| Europe | 1952 | 64.40850 |
| Europe | 2007 | 77.64860 |
| Oceania | 1952 | 69.25500 |
| Oceania | 2007 | 80.71950 |
| continent | min_l | max_l | dif |
|---|---|---|---|
| Asia | 46.31439 | 70.72848 | 24.41409 |
| Americas | 53.27984 | 73.60812 | 20.32828 |
| Africa | 39.13550 | 54.80604 | 15.67054 |
| Europe | 64.40850 | 77.64860 | 13.24010 |
| Oceania | 69.25500 | 80.71950 | 11.46450 |
#- el primer trozo sigue siendo común
aa <- gapminder %>% filter(year %in% c(1952, 2007)) %>%
group_by(continent, year) %>%
summarise(media = mean(lifeExp)) %>% ungroup()
#- usamos lag()
bb <- aa %>% group_by(continent) %>%
arrange(year) %>%
mutate(variacion = media - lag(media)) %>% ungroup()
#- para mostrar los resultados
cc <- bb %>% filter(year == 2007) %>% arrange(desc(variacion))| continent | year | media | variacion |
|---|---|---|---|
| Africa | 1952 | 39.13550 | NA |
| Americas | 1952 | 53.27984 | NA |
| Asia | 1952 | 46.31439 | NA |
| Europe | 1952 | 64.40850 | NA |
| Oceania | 1952 | 69.25500 | NA |
| Africa | 2007 | 54.80604 | 15.67054 |
| Americas | 2007 | 73.60812 | 20.32828 |
| Asia | 2007 | 70.72848 | 24.41409 |
| Europe | 2007 | 77.64860 | 13.24010 |
| Oceania | 2007 | 80.71950 | 11.46450 |
| continent | year | media | variacion |
|---|---|---|---|
| Asia | 2007 | 70.72848 | 24.41409 |
| Americas | 2007 | 73.60812 | 20.32828 |
| Africa | 2007 | 54.80604 | 15.67054 |
| Europe | 2007 | 77.64860 | 13.24010 |
| Oceania | 2007 | 80.71950 | 11.46450 |
#- ya sabemos que la primera parte es común
aa <- gapminder %>%
filter(year %in% c(1952, 2007)) %>%
group_by(continent, year) %>%
summarise(media = mean(lifeExp)) %>% ungroup()
#- pero ahora usamos pivot_wider()
bb <- aa %>% pivot_wider(names_from = year, values_from = media) %>%
mutate(dif_l = 2007 - 1952) %>%
arrange(desc(dif_l))¿qué error cometí?
| continent | year | media |
|---|---|---|
| Africa | 1952 | 39.13550 |
| Africa | 2007 | 54.80604 |
| Americas | 1952 | 53.27984 |
| Americas | 2007 | 73.60812 |
| Asia | 1952 | 46.31439 |
| Asia | 2007 | 70.72848 |
| Europe | 1952 | 64.40850 |
| Europe | 2007 | 77.64860 |
| Oceania | 1952 | 69.25500 |
| Oceania | 2007 | 80.71950 |
| continent | 1952 | 2007 | dif_l |
|---|---|---|---|
| Africa | 39.13550 | 54.80604 | 55 |
| Americas | 53.27984 | 73.60812 | 55 |
| Asia | 46.31439 | 70.72848 | 55 |
| Europe | 64.40850 | 77.64860 | 55 |
| Oceania | 69.25500 | 80.71950 | 55 |
bb <- aa %>% pivot_wider(names_from = year, values_from = media) %>%
1 mutate(dif_l = `2007` - `1952`) %>%
arrange(desc(dif_l))2007 - 1952 resta dos números (siempre da 55). Para referirnos a las columnas que se llaman 2007 y 1952 (nombres “no sintácticos”, porque empiezan por un número) hay que usar comillas invertidas: `2007`
preguntas de verdad (II)
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) %>%
ungroup()
#- por ejemplo veamos el resultado para Europe
bb <- aa %>% filter(continent == "Europe")| continent | year | mean_life | incre_mean_life_0 | incre_mean_life_t |
|---|---|---|---|---|
| Europe | 1952 | 64.40850 | 0.000000 | NA |
| Europe | 1957 | 66.70307 | 2.294567 | 2.2945667 |
| Europe | 1962 | 68.53923 | 4.130733 | 1.8361667 |
| Europe | 1967 | 69.73760 | 5.329100 | 1.1983667 |
| Europe | 1972 | 70.77503 | 6.366533 | 1.0374333 |
| Europe | 1977 | 71.93777 | 7.529267 | 1.1627333 |
| Europe | 1982 | 72.80640 | 8.397900 | 0.8686333 |
| Europe | 1987 | 73.64217 | 9.233667 | 0.8357667 |
| Europe | 1992 | 74.44010 | 10.031600 | 0.7979333 |
| Europe | 1997 | 75.50517 | 11.096667 | 1.0650667 |
| Europe | 2002 | 76.70060 | 12.292100 | 1.1954333 |
| Europe | 2007 | 77.64860 | 13.240100 | 0.9480000 |
a ver si entendéis este ejemplo
calculando CRECIMIENTOS
aa <- gapminder %>%
1 select(country, year, lifeExp) %>%
2 group_by(country) %>%
3 arrange(year) %>%
4 mutate(crecimiento = lifeExp - lag(lifeExp)) %>%
5 ungroup() %>%
6 filter(country == "Spain")group_by() es una buena práctica hacer después un ungroup()
| country | year | lifeExp | crecimiento |
|---|---|---|---|
| Spain | 1952 | 64.94 | NA |
| Spain | 1957 | 66.66 | 1.72 |
| Spain | 1962 | 69.69 | 3.03 |
| Spain | 1967 | 71.44 | 1.75 |
| Spain | 1972 | 73.06 | 1.62 |
| Spain | 1977 | 74.39 | 1.33 |
| Spain | 1982 | 76.30 | 1.91 |
| Spain | 1987 | 76.90 | 0.60 |
| Spain | 1992 | 77.57 | 0.67 |
| Spain | 1997 | 78.77 | 1.20 |
| Spain | 2002 | 79.78 | 1.01 |
| Spain | 2007 | 80.94 | 1.16 |
calculando CRECIMIENTOS ACUMULADOS
aa <- gapminder %>%
select(country, year, lifeExp) %>%
1 group_by(country) %>%
2 arrange(year) %>%
3 mutate(crec_1 = lifeExp - lag(lifeExp)) %>%
4 mutate(crec_acu_1 = cumsum(crec_1)) %>%
5 mutate(crec_acu_2 = lifeExp - first(lifeExp)) %>%
6 ungroup() %>%
filter(country == "Spain") cumsum() pero no va a funcionar: saldrá todo NA
group_by() es una buena práctica hacer después un ungroup()
| country | year | lifeExp | crec_1 | crec_acu_1 | crec_acu_2 |
|---|---|---|---|---|---|
| Spain | 1952 | 64.94 | NA | NA | 0.00 |
| Spain | 1957 | 66.66 | 1.72 | NA | 1.72 |
| Spain | 1962 | 69.69 | 3.03 | NA | 4.75 |
| Spain | 1967 | 71.44 | 1.75 | NA | 6.50 |
| Spain | 1972 | 73.06 | 1.62 | NA | 8.12 |
| Spain | 1977 | 74.39 | 1.33 | NA | 9.45 |
| Spain | 1982 | 76.30 | 1.91 | NA | 11.36 |
| Spain | 1987 | 76.90 | 0.60 | NA | 11.96 |
| Spain | 1992 | 77.57 | 0.67 | NA | 12.63 |
| Spain | 1997 | 78.77 | 1.20 | NA | 13.83 |
| Spain | 2002 | 79.78 | 1.01 | NA | 14.84 |
| Spain | 2007 | 80.94 | 1.16 | NA | 16.00 |
calculando TASAS DE CRECIMIENTO (en %)
aa <- gapminder %>%
select(country, year, gdpPercap) %>%
group_by(country) %>%
arrange(year) %>%
1 mutate(crec = gdpPercap - lag(gdpPercap)) %>%
2 mutate(tasa = crec / lag(gdpPercap) * 100) %>%
3 mutate(tasa_acu = (gdpPercap / first(gdpPercap) - 1) * 100) %>%
4 mutate(indice = gdpPercap / first(gdpPercap) * 100) %>%
ungroup() %>%
filter(country == "Spain")gapminder hay un dato cada 5 años, así que es la tasa del quinquenio, no la anual
| country | year | gdpPercap | crec | tasa | tasa_acu | indice |
|---|---|---|---|---|---|---|
| Spain | 1952 | 3,834.0 | NA | NA | 0.0 | 100.0 |
| Spain | 1957 | 4,564.8 | 730.8 | 19.1 | 19.1 | 119.1 |
| Spain | 1962 | 5,693.8 | 1,129.0 | 24.7 | 48.5 | 148.5 |
| Spain | 1967 | 7,993.5 | 2,299.7 | 40.4 | 108.5 | 208.5 |
| Spain | 1972 | 10,638.8 | 2,645.2 | 33.1 | 177.5 | 277.5 |
| Spain | 1977 | 13,236.9 | 2,598.2 | 24.4 | 245.2 | 345.2 |
| Spain | 1982 | 13,926.2 | 689.2 | 5.2 | 263.2 | 363.2 |
| Spain | 1987 | 15,765.0 | 1,838.8 | 13.2 | 311.2 | 411.2 |
| Spain | 1992 | 18,603.1 | 2,838.1 | 18.0 | 385.2 | 485.2 |
| Spain | 1997 | 20,445.3 | 1,842.2 | 9.9 | 433.3 | 533.3 |
| Spain | 2002 | 24,835.5 | 4,390.2 | 21.5 | 547.8 | 647.8 |
| Spain | 2007 | 28,821.1 | 3,985.6 | 16.0 | 651.7 | 751.7 |
Una vez hemos contado, muchas veces interesa saber el porcentaje que representa cada grupo respecto al total
Para calcular esos porcentajes, hay que dividir el nº de observaciones de cada grupo entre el total. Veamos cómo hacerlo
calculando PORCENTAJES (con R à la tidyverse)
#- Calcular % de población de cada continente
aa <- gapminder %>%
1 group_by(continent, year) %>%
2 summarise(pob_continent = sum(pop, na.rm = TRUE)) %>% ungroup() %>%
3 group_by(year) %>%
4 mutate(pob_mundo = sum(pob_continent),
5 pob_percent = pob_continent/pob_mundo * 100) %>%
ungroup()1bb <- aa %>% filter(year %in% c(1952, 2007))| continent | year | pob_continent | pob_mundo | pob_percent |
|---|---|---|---|---|
| Africa | 1952 | 237640501 | 2406957150 | 9.87 |
| Africa | 2007 | 929539692 | 6251013179 | 14.87 |
| Americas | 1952 | 345152446 | 2406957150 | 14.34 |
| Americas | 2007 | 898871184 | 6251013179 | 14.38 |
| Asia | 1952 | 1395357351 | 2406957150 | 57.97 |
| Asia | 2007 | 3811953827 | 6251013179 | 60.98 |
| Europe | 1952 | 418120846 | 2406957150 | 17.37 |
| Europe | 2007 | 586098529 | 6251013179 | 9.38 |
| Oceania | 1952 | 10686006 | 2406957150 | 0.44 |
| Oceania | 2007 | 24549947 | 6251013179 | 0.39 |
Extensión: tabla con la importancia (en términos de población) de cada continente en el tiempo
| continent | year | pob_continent | pob_mundo | pob_percent |
|---|---|---|---|---|
| Africa | 1952 | 237640501 | 2406957150 | 9.9 |
| Americas | 1952 | 345152446 | 2406957150 | 14.3 |
Ahora quiero hacer la tabla presentable: he de pasarla a formato ancho
gt::gt(bb) %>%
gt::fmt_number(2, sep_mark = ".", decimals = 0) %>%
gt::fmt_number(3:7, decimals = 1)| year | pob_mundo | Africa | Americas | Asia | Europe | Oceania |
|---|---|---|---|---|---|---|
| 1952 | 2.406.957.150 | 9.9 | 14.3 | 58.0 | 17.4 | 0.4 |
| 1957 | 2.664.404.580 | 9.9 | 14.5 | 58.7 | 16.4 | 0.4 |
| 1962 | 2.899.782.974 | 10.2 | 14.9 | 58.5 | 15.9 | 0.5 |
| 1967 | 3.217.478.384 | 10.4 | 14.9 | 59.2 | 15.0 | 0.5 |
| 1972 | 3.576.977.158 | 10.6 | 14.8 | 60.1 | 14.0 | 0.5 |
| 1977 | 3.930.045.807 | 11.0 | 14.7 | 60.7 | 13.2 | 0.4 |
| 1982 | 4.289.436.840 | 11.6 | 14.7 | 60.9 | 12.4 | 0.4 |
| 1987 | 4.691.477.418 | 12.3 | 14.6 | 61.2 | 11.6 | 0.4 |
| 1992 | 5.110.710.260 | 12.9 | 14.5 | 61.3 | 10.9 | 0.4 |
| 1997 | 5.515.204.472 | 13.5 | 14.4 | 61.3 | 10.3 | 0.4 |
| 2002 | 5.886.977.579 | 14.2 | 14.4 | 61.2 | 9.8 | 0.4 |
| 2007 | 6.251.013.179 | 14.9 | 14.4 | 61.0 | 9.4 | 0.4 |
calculando RANKINGs
aa <- gapminder %>%
1 select(country, year, lifeExp) %>%
2 group_by(year) %>%
3 mutate(rank_1 = row_number(desc(lifeExp))) %>%
mutate(rank_2 = min_rank(desc(lifeExp)) ) %>%
#- también puedo calcular el ranking a mano
4 arrange(desc(lifeExp)) %>%
mutate(rank_mio = 1:n()) %>%
ungroup() row_number()). Para ello, antes, he de ordenar las observaciones por desc(lifeExp)
| country | year | lifeExp | rank_1 | rank_2 | rank_mio |
|---|---|---|---|---|---|
| Spain | 2007 | 80.9 | 6 | 6 | 6 |
| Spain | 2002 | 79.8 | 8 | 8 | 8 |
| Spain | 1997 | 78.8 | 8 | 8 | 8 |
| Spain | 1992 | 77.6 | 7 | 7 | 7 |
| Spain | 1987 | 76.9 | 5 | 5 | 5 |
| Spain | 1982 | 76.3 | 4 | 4 | 4 |
| Spain | 1977 | 74.4 | 8 | 8 | 8 |
| Spain | 1972 | 73.1 | 8 | 8 | 8 |
| Spain | 1967 | 71.4 | 10 | 10 | 10 |
| Spain | 1962 | 69.7 | 18 | 18 | 18 |
| Spain | 1957 | 66.7 | 26 | 26 | 26 |
| Spain | 1952 | 64.9 | 24 | 24 | 24 |
Extensión: rankings con empates [🌶]
row_number(), min_rank() y dense_rank() solo se diferencian cuando hay empates:df <- tibble(nota = c(10, 9, 9, 7)) %>%
1 mutate(r_row = row_number(desc(nota)),
2 r_min = min_rank(desc(nota)),
3 r_dense = dense_rank(desc(nota)))ifelse() y case_when()ifelse(): ejecuta “algo” de manera condicional
case_when(): es una generalización de ifelse()
ifelse(): ejecuta “algo” de manera condicional
veamos algunos ejemplos
aa <- gapminder %>%
select(-pop) %>%
mutate(X1 = ifelse(lifeExp > 70, "longevo", "no longevo")) %>%
mutate(X2 = ifelse(gdpPercap > 10000, "rico", "pobre")) %>%
mutate(X3 = ifelse(lifeExp > 70 & gdpPercap > 10000,
"longevo y rico",
"no longevo y/o pobre")) %>%
mutate(X4 = ifelse(continent == "Europe", "europeo", "no europeo")) el resultado es:
aa %>% filter(country %in% c("Spain", "Angola")) %>%
filter(year %in% c(1952, 2007)) %>%
gt::gt() %>%
gt::opt_stylize(style = 3) %>%
gt::cols_align(align = "center")| country | continent | year | lifeExp | gdpPercap | X1 | X2 | X3 | X4 |
|---|---|---|---|---|---|---|---|---|
| Angola | Africa | 1952 | 30.015 | 3520.610 | no longevo | pobre | no longevo y/o pobre | no europeo |
| Angola | Africa | 2007 | 42.731 | 4797.231 | no longevo | pobre | no longevo y/o pobre | no europeo |
| Spain | Europe | 1952 | 64.940 | 3834.035 | no longevo | pobre | no longevo y/o pobre | europeo |
| Spain | Europe | 2007 | 80.941 | 28821.064 | longevo | rico | longevo y rico | europeo |
case_when(): es una generalización de ifelse()
A ver si entiendes este ejemplo:
aa <- gapminder %>%
group_by(continent, year) %>%
mutate(media_lifeExp = mean(lifeExp)) %>%
mutate(media_gdpPercap = mean(gdpPercap)) %>%
1 mutate(GOOD_or_BAD = case_when(
2 lifeExp > media_lifeExp & gdpPercap > media_gdpPercap ~ "good",
3 lifeExp < media_lifeExp & gdpPercap < media_gdpPercap ~ "bad" ,
4 .default = "medium" ) )case_when() va comprobando las condiciones en orden; en cuanto una se cumple, asigna su valor
El resultado es:
aa %>% ungroup() %>% filter(country == "Spain", year <= 1957) %>% select(-pop) %>%
gt::gt() %>%
gt::fmt_number(4:7, sep_mark = ".", dec_mark = ",", decimals = 2) %>%
gt::opt_stylize(style = 2) %>%
gt::cols_align(align = "center")| country | continent | year | lifeExp | gdpPercap | media_lifeExp | media_gdpPercap | GOOD_or_BAD |
|---|---|---|---|---|---|---|---|
| Spain | Europe | 1952 | 64,94 | 3.834,03 | 64,41 | 5.661,06 | medium |
| Spain | Europe | 1957 | 66,66 | 4.564,80 | 66,70 | 6.963,01 | bad |
crear grupos a partir de una variable numérica: ntile() y cut()
case_when(), pero hay 2 funciones que lo hacen más rápidoaa <- gapminder %>%
filter(year == 2007) %>%
select(country, gdpPercap) %>%
1 mutate(quintil = ntile(gdpPercap, 5)) %>%
2 mutate(tramo = cut(gdpPercap,
3 breaks = c(0, 2000, 10000, Inf),
4 labels = c("bajo", "medio", "alto")))ntile() divide las observaciones en grupos con el mismo nº de observaciones: con 5 son quintiles (el 1 es el 20% de países más pobres y el 5 el 20% más ricos)
cut() (de R-base) divide en tramos que elegimos nosotros
Hasta ahora hemos trabajado con un único df, pero …
… muchas veces tenemos que trabajar con datos que están en varias tablas
dos casos ideales (sencillos de unir): bind_cols() y bind_rows()
A) Si los 2 dfs tienen exactamente las mismas filas o unidades de análisis (y además en el mismo orden)
B) Si los 2 dfs tienen exactamente las mismas columnas (y además en el mismo orden)
joining df’s
Hay diversos tipos de uniones de tablas: un buen recurso para estos temas es el capítulo dedicado a JOINS en R4DS2ed
Las uniones más comunes son las “mutating joins”, que añaden columnas de una tabla a otra. Lo vemos:
mutating joins (juntando df1 con df2)
… se diferencian en las filas que se seleccionan
Las filas que se seleccionan dependen del criterio para hacer el match. Lo vemos:
tipos de mutating joins
Recuerda que todas ellas añaden columnas (las columnas de df2 a df1) pero que se diferencian en las filas que se seleccionan
inner_join(df1,df2): retorna las filas de df1 que también existen en df2left_join(df1,df2): retorna TODAS las filas de df1full_join(df1,df2): retorna TODAS las filas de df1 y de df2; (o sea, retorna TODAS las filas y TODAS las columnas de las 2 tablas)right_join(df1, df2), que retorna TODAS las filas de df2; es como un left_join() con las tablas al revés)mutating joins: un ejemplo
mutating joins: un ejemplo
by = "id" le decimos qué columna usar para hacer el match. Si no lo ponemos, R usa las columnas con el mismo nombre en las 2 tablas (y avisa con un mensaje). Mejor ponerlo siempreExtensión: cuando las claves no coinciden [🌶]
join_by()by = c("id" = "codigo")country, cada fila de pob se juntaría con las 12 filas (los 12 años) de ese país en rentaExtensión: filtering joins [🌶]
semi_join(): por ejemplo, en una encuesta de presupuestos familiares, para quedarte con el gasto de los hogares que aparecen en la tabla de hogares con hipotecaTarea: fusionar dos tablas
gapminder trae otra tabla, gapminder::country_codes, con los códigos ISO de cada país| country | iso_alpha | iso_num |
|---|---|---|
| Afghanistan | AFG | 4 |
| Albania | ALB | 8 |
| Algeria | DZA | 12 |
gapminder las columnas con los códigos ISO de cada paísgapminder y añadirle columnasby)| country | continent | year | lifeExp | pop | gdpPercap | iso_alpha | iso_num |
|---|---|---|---|---|---|---|---|
| Afghanistan | Asia | 1952 | 28.801 | 8425333 | 779.4453 | AFG | 4 |
| Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.8530 | AFG | 4 |
| Afghanistan | Asia | 1962 | 31.997 | 10267083 | 853.1007 | AFG | 4 |
| Afghanistan | Asia | 1967 | 34.020 | 11537966 | 836.1971 | AFG | 4 |
gapminder sin código? Compruébalo con anti_join(gapminder, codigos, by = "country")| Para … | usamos … |
|---|---|
| seleccionar columnas | select() (y relocate(), rename()) |
| seleccionar filas | filter(), slice() y sus variantes, distinct() |
| crear o modificar columnas | mutate() (con ifelse(), case_when(), lag(), cumsum(), …) |
| ordenar filas | arrange() |
| resumir | summarise() (con n(), n_distinct(), mean(), …), count() |
| hacer cálculos por grupos | group_by() (y ungroup()) |
| fusionar tablas | bind_rows(), bind_cols(), left_join(), inner_join(), full_join() |
dplyrEn el siguiente tema: gráficos con ggplot2