Usaremos el dataset “nycityflights13” para practicar con dplyr
library(tidyverse)
## -- Attaching packages ----------------------------------------------------------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.0 v purrr 0.2.5
## v tibble 1.4.2 v dplyr 0.7.8
## v tidyr 0.8.2 v stringr 1.3.1
## v readr 1.1.1 v forcats 0.3.0
## -- Conflicts -------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(nycflights13) # cargamos el dataset de los aviones
head(nycflights13::flights)
Estos datasets se sacan del BUREAU OF TRANSPORTATION STATISTICS.
Este objeto no es un dataset, es un tibble, es decir un dataframe “tuneado” para trabajar mejor con las herramientas de tidyverse. Debajo de los columnames nos aparecen el formato para cada una de las variables. La información que nos da es:
int -> números enteros
dbl -> números reales (double)
chr -> vector de carácteres o strings
dttm -> date + time
lgl -> logical, contiene balores booleanos (True or False)
fctr -< factor, variables categóricas
date -> fecha(día, mes y año)
En esta sección aprenderemos a usar 5 funciones básicas que nos ayudaran a trabajar este data set:
filter() -> filtrar observaciones a partir de valores concretos
arrange() -> reordenar las filas
select() -> seleccionar las variables según sus nombres
mutate() -> crea nuevas variables con funciones a partir de la variables existentes
summarise() -> colapsar varios valores para dar un resumen de los mismos
Todas estas funciones la acompañaremos con group_by(), que opera la función a la que acompaña grupo a grupo.
Estas funciones siempre tendrán la misma estructura:
data frame que usaremos
operaciones que queremos hacer a las variables del data frame
resultados en un nuevo data frame
Filter() nos ayuda a obtener un subconjunto de valores basados en una serie de valores.
Imaginemos que queremos obtener todos los vuelos del 1 de enero:
head(jan1 <- filter(flights, month == 1, day == 1)) # redondeando con paréntesis puedo ejecutar la variable a la vez que le asigno un valor
Los operadores logísticos que usaremos para filtrar son; > , < , >= , <= , == , !=
Tenemos también la función near, para poder solucionar problemas numéricos que hayan con el ordenador:
sqrt(2)^2 == 2 # debería ser TRUE porque el quadrado de la raíz quadrada se eliminan entre ellos, sin embargo, el resultado es falso. Para solcionarlo usamos near():
## [1] FALSE
near(sqrt(2)^2,2)
## [1] TRUE
Imagen explicativa de cómo funcionan las condiciones booleanas en R:
imagen de las condiciones booleanas
EL conjunto total de operaciones booleanas que se pueden llevar a cabo son:
imagen de las operaciones booleanas
¿Cómo aplicarlo con filter?
head(filter(flights, month == 5 | month == 6)) # devuelveme los meses de mayo y junio
Podemos hacer un filtraje según el valor de dos meses seleccionando como vector:
head(may_june <- filter(flights, month %in% c(5,6)))
Tener en mente la ley de morgan para valores booleanas:
!(x&y) == (!x)|(!y)
!(x|y) == (!x)&(!y)
Por ejemplo, si queremos ver los vuelos que no fueran retrasados más de 60 minutos ni en salida ni en llegada, usamos el siguiente código:
head(filter(.data = flights, !(arr_delay > 60 | dep_delay > 60)))
Aún que la función previa funciona, sería más óptimo escribir:
head(filter(.data = flights, arr_delay <= 60, dep_delay <= 60))
¿Cómo gestionar los NAs? Primero creamos el dataset:
(df <- tibble(x = c(1,2,NA,4,5)))
Filter nos permite evadir los NA:
filter(df, x>2)
Si queréis preservar los valores de NA, se le debe pedir explícitamente en el filter:
filter(df, is.na(x) | x>2)
Opciones de visualización de un dataset de forma rápida. Ver las primeras 5 filas:
head(flights, 5)
Ver las útlimas 5 filas:
tail(flights, 5)
Arrange nos devolverá el mismo número de filas del dataset pero ordenadas de forma que hayamos seleccionado:
sorted_date <- arrange(.data = flights, year, month, day)
tail(sorted_date)
Podemos ordenar de mayor a menor gracias a desc():
head(arrange(flights, desc(arr_delay))) # ordenar los vuelos por los que tardaron más en llegar hasta el que menos
Si se usa la función de arrange para ordenar un vector que tenga NAs, estos siempre acabarán al final:
arrange(df,x)
Si queremos ver por ejemplo los vuelos que recorrieron menos distancia:
head(arrange(flights, distance))
Si queremos saber cual es el vuelo que más recorrió:
head(arrange(flights, desc(distance)))
Podemos hacer uso la función between(), para buscar valores entre determinados valores. Por ejemplo, si quisieramos saber los vuelos entre las 0:00 horas y las 6:00 horas:
head(filter(flights, between(hour, 0, 6)))
O si quisieramos saber cuales son los valores desconocidos en una variable en concreto:
head(filter(.data = flights, is.na(dep_time)))
Otras opciones con arrange:
# ¿Cuál fué el vuelo con más retraso?
arrange(flights, desc(dep_delay))[1,]
# ¿Cuál fué el vuelo con menos retraso?
arrange(flights, dep_delay)[1,]
flights %>%
filter(arr_delay > 60)
flights %>%
filter(dest == "SFO" | dest == "OAK")
flights %>%
filter(carrier == "UA" | carrier == "AA")
flights %>%
mutate(month = months(time_hour)) %>%
filter(month == "abril" | month == "mayo" | month == "junio" )
flights %>%
filter(arr_delay > 60 & dep_delay <= 60)
flights %>%
filter(dep_delay > 60 & arr_delay <= 30)
library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
##
## date
flights %>%
filter(hour(time_hour) > 0 & hour(time_hour) < 7)
library(lubridate)
flights %>%
filter(between(hour(time_hour), 0, 7 ))
flights %>%
filter(is.na(dep_time)) %>%
group_by(dep_time) %>%
summarise(n = n())
flights[is.na(flights),]
## Warning: Length of logical index must be 1 or 336776, not 6398744
NA^0
NA|TRUE
FALSE&NA
Intenta establecer la regla general para saber cuando es o no es NA (cuidado con NA*0)
NA^0
## [1] 1
NA|TRUE
## [1] TRUE
FALSE&NA
## [1] FALSE
is.na(NA)
## [1] TRUE
Vamos a usar la función select con un determinado subgrupo, seleccionando solo las variables de dep_delay y arr_delay:
select(sorted_date[1024:1027,], dep_delay, arr_delay)
Podemos seleccionar desde una variable a otra, incluyendo todas las que están por medio:
select(sorted_date[1024:1027,], dep_time:arr_delay)
O excluir un conjunto de columnas:
select(sorted_date[1024:1027,], -(dep_delay:arr_delay))
Select tiene muchas utilidades, por ejemplo, seleccionar solo las columnas que empiecen por “dep”:
select(sorted_date[1024:1027,], starts_with("dep"))
O las que acaben con “delay”:
select(sorted_date[1024:1027,], ends_with("delay"))
O que contenga “st”:
select(sorted_date[1024:1027,], contains("st"))
Podemos buscar un rango número también. Sin embargo, en el dataset de aviones no hay ninguna variable así:
select(flights, num_range("x", 1:5)) # x1, x2, x3, x4, x5
La función one_of() de dplyr nos permite añadir variables en string dentro de un vector. Muy útil si es el resultado de un programa que ha devuelto un array de variables que queremos seleccionar automaticamente:
head(select(flights, one_of(c("year", "month", "day", "dep_delay", "arr_delay"))))
Usaremos la función rename() para realizar esta acción:
head(rename(flights, deptime = dep_time, any = year, mes = month, dia = day))
Podemos ordenar las columnas con select para visualizaciones rápidas. La función everything() nos permitirá seleccionar todas las otras variables restantes:
head(select(flights, time_hour, distance, air_time, everything()))
1.Piensa cómo podrías usar la función arrange() para colocar todos los valores NA al inicio. Pista: puedes la función is.na() en lugar de la función desc() como argumento de arrange.
x <- tibble(x = c(1,2,NA,NA,6,4,3),
y = c(8,NA,2,NA,NA,6,3))
x %>%
arrange(desc(is.na(y)))
names(flights)
## [1] "year" "month" "day" "dep_time"
## [5] "sched_dep_time" "dep_delay" "arr_time" "sched_arr_time"
## [9] "arr_delay" "carrier" "flight" "tailnum"
## [13] "origin" "dest" "air_time" "distance"
## [17] "hour" "minute" "time_hour"
flights %>% select(flight, dep_delay) %>% arrange(desc(dep_delay))
tail(arrange(.data = flights,desc(dep_delay)))
Mutate() siempre añade columnas al final del dataset, así que es mejor crear un nuevo data set con el que trebajaremos, y mantener apartado el original. Creamos el nuevo dataset con el que trabajaremos:
flights_new <- select(flights,
year:day,
ends_with("delay"),
distance,
air_time)
Ahora calcularemos nuevas variables con la función mutate():
head(mutate(flights_new,
time_gain = arr_delay - dep_delay, # diferencia de tiempo (min)
flight_speed = distance/(air_time/60), # velocidad = espacio * tiempo (km/h)
air_time_hour = air_time/60,
time_gain_per_hour = time_gain / air_time_hour
) -> flights_new) # las asignamos al dataset que hemos creado para tratar
Si lo que queremos es modificar las variables y eliminarlas, usaremos la función transmute().
head(transmute(flights_new,
time_gain = arr_delay - dep_delay, # diferencia de tiempo (min)
flight_speed = distance/(air_time/60), # velocidad = espacio * tiempo (km/h)
air_time_hour = air_time/60,
time_gain_per_hour = time_gain / air_time_hour
) -> data_from_flights_new)
Mutate() es una función que solo puede operar con vectores. Es decir que se puede deber tomar un vector como parámetro para operar y obtener un vector de salida igual de largo.
Operaciones que podemos hacer dentro de mutate:
operaciones aritméticas: +, -, *, /, ^
agregados de funciones: x/suma(x) : proporción sobre el total x - mean(x) : distancia respecto de media (x - mean(x))/sd(x) : tipificación (x - min(x))/(max(x) - min(x)) : estandarizar entre [0,1]
aritmética modular: %/% -> cociente de la división entera %% -> resto de la división entera x == y * (x%/%y) + (x%%y)
Imaginemos que quisieramos tener las horas y minutos de un vuelo determinado:
head(transmute(flights_new,
air_time,
hour_air = air_time %/% 60,
minute_air = air_time %% 60))
logaritmos: log() -> logaritmo en base e log2() log10()
offsets: lead() -> mueve hacia la izquierda lag() -> mueve las posiciones hacia la derecha
df <- 1:12
# derecha
lag(df)
## [1] NA 1 2 3 4 5 6 7 8 9 10 11
# izquierda
lead(df)
## [1] 2 3 4 5 6 7 8 9 10 11 12 NA
cumsum(df)
## [1] 1 3 6 10 15 21 28 36 45 55 66 78
cumprod(df)
## [1] 1 2 6 24 120 720 5040
## [8] 40320 362880 3628800 39916800 479001600
cummin(df)
## [1] 1 1 1 1 1 1 1 1 1 1 1 1
cummax(df)
## [1] 1 2 3 4 5 6 7 8 9 10 11 12
cummean(df)
## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5
head(transmute(flights,
dep_delay,
has_been_delayed = (dep_delay > 0)))
df <- c(3,(-2),1,0,NA,(-1),2,(-3))
min_rank(df) # nos orena de menos a mas, de -3 a 3
## [1] 7 2 5 4 NA 3 6 1
df
## [1] 3 -2 1 0 NA -1 2 -3
Si queremos hacerlo a la inversa sería:
min_rank(desc(df))
## [1] 1 6 3 4 NA 5 2 7
df
## [1] 3 -2 1 0 NA -1 2 -3
Podemos usar otras funciones como dense_rank(), rwon_number() y percent_rank()
percent_rank(df) # porcentage según e mínimo de una fila vs el máximo
## [1] 1.0000000 0.1666667 0.6666667 0.5000000 NA 0.3333333 0.8333333
## [8] 0.0000000
O otra funciones como cume_dist(df), que es el porcentage rank acumulado
cume_dist(df)
## [1] 1.0000000 0.2857143 0.7142857 0.5714286 NA 0.4285714 0.8571429
## [8] 0.1428571
O si queremo tenerlo ordenado por percentiles:
ntile(df, n = 4)
## [1] 4 1 3 2 NA 2 3 1
summarise() nos permite resumir todo el data set en una variable determinada
summarise(flights, delay = mean(dep_delay, na.rm = T))
Por si solo summarise no nos es muy útil pero si la combinamos con otras funciones como group_by(), nos ayudará a ver mejores resultados:
by_month_group <- group_by(flights, year, month, day)
head(summarise(by_month_group,
delay = mean(dep_delay, na.rm = T), # calculo del promedio
median = median (dep_delay, na.rm = T),
min = min(dep_delay, na.rm = T)
))
¿Cómo podríamos saber cuál es la compañia que se retrasa más?
mutate(summarise(group_by(flights, carrier),
delay = mean(dep_delay, na.rm = T)),
sorted = min_rank(delay)) # la compañia US tiene menos delay
Es un operador que nos funciona de forma ideal entre group_by() y summarise(). Empecemos con una situacion de analisis sin pipes, y luego repetiremos el proceso con pipe:
group_by_dest <- group_by(flights, dest)
delay <- summarise(group_by_dest,
count = n(),
dist = mean(distance, na.rm = T),
delay = mean(arr_delay, na.rm = T))
delay <- filter(delay, count>100, dest != "HNL") # filtramos quitando los aeropuertos que no queremos, ejemplo, HNL que es Honolulu queda muy lejos (outlier)
# Crearemos un plot para visualizario los datos:
ggplot(data = delay, mapping = aes(x = dist, y = delay))+
geom_point(aes(size = count), alpha = 0.2) +
geom_smooth(se = F) +
geom_text(aes(label = dest), alpha = .4)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
La sintaxis de pipes pretende juntar una serie de operaciones como las anteriores a través de “%>%”:
delays <- flights %>%
group_by(dest) %>%
summarise(count = n(),
dist = mean(distance, na.rm = T),
delay = mean(arr_delay, na.rm = T)) %>%
filter(count > 100, dest != "HNL") %>%
ggplot(mapping = aes(x = dist, y = delay))+
geom_point(aes(size = count), alpha = 0.2) +
geom_smooth(se = F) +
geom_text(aes(label = dest), alpha = .4)
delays
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Como tratar los NA dentro del dataset de flights:
flights %>%
group_by(year,month,day) %>%
summarise(mean = mean(dep_delay, na.rm = T),
median = median(dep_delay, na.rm = T),
sd = sd(dep_delay, na.rm = T),
count = n())
Con na.rm = T eliminamos los NA del análisis, pero los NA nos pueden dar valor. En este caso, para el dataset de flights, nos dicen los vuelos cancelados, por lo tanto vamos a ver como podemos darle valor:
flights %>%
filter(!is.na(dep_delay), !is.na(arr_delay))%>%
group_by(year,month,day) %>%
summarise(mean = mean(dep_delay, na.rm = T),
median = median(dep_delay, na.rm = T),
sd = sd(dep_delay, na.rm = T),
count = n())
Creación de un dataset sin NAs
not_cancelled <- flights %>%
filter(!is.na(dep_delay), !is.na(arr_delay))
Trabajaremos con un dataset de la liga de béisbol, primero cargamos la libreria donde está:
library(Lahman)
¿Como evaluamos como de bueno es un bateador? Miramos las veces que ha bateado (H, de hit) y el numero de veces que pudo batear (AB):
Batting <- as.tibble(Lahman::Batting)
Con tibble, convertiremos la informacion a un objeto tipo tibble y así poder ejecutar mejor las funciones pipe:
batters <- Batting %>%
group_by(playerID) %>%
summarise(hits = sum(H, na.rm = T),
bats = sum(AB, na.rm = T),
bat.average = hits/bats)
Ahora ya tenemos los datos de los bateadores en la variable batters. Vamos a hacer unplot para ver sus datos
batters %>%
filter(bats > 100) %>%
ggplot(mapping = aes(x = bats, y = bat.average))+
geom_point(alpha = 0.2)+
geom_smooth(se=F)
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
El gràdico nos muestra que hay una buena relacion entre las veces que bateas y las veces que los jugadores le dan a la bola. ¿Como sabemos que no es fruto de la aleatoriedad?
head(batters) %>%
filter(bats > 100) %>%
arrange(desc(bat.average))
Las conclusiones es que realmente los que mas han acertado han bateado muchas veces.
Vamos a separar las diferentes medidas entre familias:
media
mediana
not_cancelled %>%
group_by(carrier) %>%
summarise(
mean = mean(arr_delay),
mean2 = mean(arr_delay[arr_delay > 0]), # solo los vuelos que se retrasan para saber el promedio de su retraso
median = median(arr_delay) # comparar con la media
)
Nos permite saber si hay mucha concentración de los datos o son muy variados
desviación estándard
rango intercuartílico […]
not_cancelled %>%
group_by(carrier) %>%
summarise(
sd = sd(arr_delay),
iqr = IQR(arr_delay), # rango intercuartílico
mad = mad(arr_delay) # Median Absolute Deviation
) %>%
arrange(desc(sd))# ordenar para ver los que tienen más error estandard
not_cancelled %>%
group_by(carrier) %>%
summarise(
first = min(arr_delay), # cual es le tiempo
q1 = quantile(arr_delay, 0.25),
median = quantile(arr_delay, 0.5), # median()
q3 = quantile(arr_delay, 0.75),
last = max(arr_delay)
)
not_cancelled %>%
group_by(carrier) %>%
summarise(
first_dep = first(dep_time), # el primer vuelo que sale
second_dep = nth(dep_time, 2),
third_dep = nth(dep_time, 3),
last_dep = last(dep_time) # elultimo vuelo que sale
)
Creación de un ranking para ver los vuelos que salen antes por compañía:
not_cancelled %>%
group_by(carrier) %>%
mutate(rank = min_rank(dep_time)) %>%
filter(rank %in% range(rank)) -> temp
head(temp)
la n como conteo
n_distinct(), evitar los los NA
flights %>%
group_by(dest) %>%
summarise(
count = n(),
carriers = n_distinct(carrier),
arrivals = sum(!is.na(arr_delay))
) %>%
arrange(desc(carriers)) -> temp
head(temp)
Si queremos saber de los vuelos no cancelados el numero de destinos:
not_cancelled %>% count(dest) -> temp
head(temp)
not_cancelled %>%
group_by(year, month, day) %>%
summarise(n_prior_5 = sum(dep_time < 500)) -> temp
head(temp)
Saber que porcentaje de vuelos tuvieron un retraso de más de 1 hora por compañía :
not_cancelled %>%
group_by(carrier) %>%
summarise(more_than_hour_delay = mean(arr_delay>60)) %>%
arrange(desc(more_than_hour_delay))
¿Cómo podríamos ir acumulando grupos? Ejemplo:
daily <- group_by(flights, year, month, day)
per_day <- summarise(daily, n_fl = n())
head(per_day)
per_month <- summarise(per_day, n_fl = sum(n_fl))
head(per_month)
(per_year <- summarise(per_month, n_fl = sum(n_fl)))
También podemos usar la función ungroup() para desagrupar las variables previamente tratadas:
daily %>%
ungroup() %>%
summarise(n_fl = n())
¿Como podríamos saber cuántos son destinos populares? Cogeríamos los destinos que recibieron más de un vuelo al día:
popular_dest <- flights %>%
group_by(dest) %>%
filter(n()>365)
head(popular_dest)