Usaremos la librería stringr para trabajar con strings. La cargamos junto con tidyverse:
library(stringr)
library(tidyverse)
## -- Attaching packages ----------------------------------------------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.0 v readr 1.1.1
## v tibble 1.4.2 v purrr 0.2.5
## v tidyr 0.8.2 v dplyr 0.7.8
## v ggplot2 3.1.0 v forcats 0.3.0
## -- Conflicts -------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
Fundamentos básicos de los strings. Se pueden crear strings a traves de “” o ’’. En caso que se quieran poner comillas dentro del string:
s1 <- "Esto es un string"
s2 <- 'Esto es un string que contiene otro "string" dentro'
Si queremos guardar una coma simple o doble dentro de un string:
double_quote <- "\"" # sería lo mismo que poner '"'
single_quote <- '\'' # "'"
backslash <- "\\"
# Juntemos los strings creados
x <- c(single_quote, double_quote, backslash)
writeLines(x) # escríbeme en lineas el string
## '
## "
## \
Ejemplo de creación de una mu:
"\u00b5"
## [1] "µ"
Usaremos las funciones del paquete “stringr”. Ejemplos de operaciones:
str_length(c("kdsfj","dmfdjsk dasjffkdsl ksadfjsjak"))
## [1] 5 29
str_c("a","b","c", sep = ", ")
## [1] "a, b, c"
x <- c("abc", NA)
str_c("hola", x, "adios", sep = " ")
## [1] "hola abc adios" NA
Si queremos usar el NA como parte del string
str_c("hola", str_replace_na(x), "adiós", sep = " ")
## [1] "hola abc adiós" "hola NA adiós"
Otro ejemplo de concatenación:
str_c("prefix-", c("a","b","c"), "-suffix")
## [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
Ejemplos de strings usados con if
name <- "Alejandro Magno"
momento_del_dia <- "tarde"
birthday <- FALSE
str_c(
"Buena ", momento_del_dia," ", name,
if(birthday) " y FELIZ CUMPLEA?OS",
"."
)
## [1] "Buena tarde Alejandro Magno."
Si queremos colapsar un vector de strings en un solo string, usaremos la función collapse():
str_c(c("a", "b", "c"), collapse = ",")
## [1] "a,b,c"
x <- c("Manzanas", "Peras", "Limones", "Platanos")
str_sub(x, 1,3) # posiciones de las cuales quieres extraer información
## [1] "Man" "Per" "Lim" "Pla"
str_sub(x, -3, -1)
## [1] "nas" "ras" "nes" "nos"
# ¿Qué pasa si nos pasamos de posición?
str_sub("x", 1,8) # no pasa nada, nos devolverá el string tan grande como sea posible
## [1] "x"
Podemos también hacer asignaciones o modificar el string de forma correcta. En este caso pondremos todos los strings a letras minúsculas:
str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))
x
## [1] "manzanas" "peras" "limones" "platanos"
O poner en mayúsculas:
str_to_upper(x)
## [1] "MANZANAS" "PERAS" "LIMONES" "PLATANOS"
O poner solo la primera letra en mayusculas:
str_to_title(x)
## [1] "Manzanas" "Peras" "Limones" "Platanos"
Sin embargo, puedes tener problemas en relación al lenguaje que se use. Por ejemplo, el turco tiene diferentes i (sin punto y con punto). Para trabajar con ellos debemos indicar la ISO:
str_to_upper("i", locale = "tr")
## [1] "I"
También se ordenan de forma distinta dependiendo del idioma. Para español se ordena alfabéticamente:
str_sort(x, locale = "es")
## [1] "limones" "manzanas" "peras" "platanos"
Sin embargo, si usamos otro idioma como el hawaiano, donde las vocales van antes que las consonantes:
str_sort(c("apple", "banana","eggplant"), locale = "haw")
## [1] "apple" "eggplant" "banana"
Las expresiones regulares ayudan a localizar un patrón dentro de un string. Usaremos dos funciones que cogen un vector como parámetro y una expresión regular y muestran si hay o no hay coincidencia.
Ejemplos:
library(htmlwidgets)
x <- c("manzana", "banana", "pera", "pomelo")
str_view(x,"an")
str_view nos marca exactamente el patron que estava buscando en el vector.
Podemos usar el str_view() con a expresión regular “.” que hace referencia a cualquier carácter a excepción de un alto de linea:
str_view(x, ".o.")
¿Cómo hago para buscar un punto? Usaremos el backslash, que nos indica que el siguiente carácter es escapante:
dot <- "\\."
writeLines(dot)
## \.
str_view(c("abc", "a.c", "bc"), dot)
# o también sería correcto:
str_view(c("abc", "a.c", "bc."), "\\.c")
Si queremos localizar un backslash, usaremos:
backslash <- "\\\\"
writeLines(backslash)
## \\
str_view("a\\b", "\\\\")
Por defecto cualquier expresión regular solo localiza una parte del string, así que es muy útil usarlo con anclas para localizar el inicio o el final de string:
^ -> inicio del string
$ -> final del string
Localizar todo lo que empieze por “p” o acabe con “a”:
str_view(x, "^p")
str_view(x, "a$")
Otro ejemplo donde solo se quiera buscar la palabra manzana sola en el string:
y <- c("tarta de manzana", "manzana", "manzana al horno", "pastel de manzana")
str_view(y, "^manzana$")
Otras expresiones regulares interesantes:
Expresiones regulares
Ejemplo para localizar el color gris en inglés (dos formas de escribirlo):
str_view(c("grey","gray"), "gr(e|a)y")
Cuantas veces aparece un determinado elemento dentro de un patrón. Herramientas:
? -> el elemento puede aparecer 0 o 1 veces
Ejemplo:
x <- "El año 1888 es el m?s largo en números romanos: MDCCCLXXXVIII"
str_view(x, "CC?")
str_view(x, "CC+")
str_view(x, "C[LX]+")
Ejemplos:
colour (“colou?r”), no se si la palabra tiene “u” o no, pero indicame la palabra
ba(na)+, busca todas las veces que “na” este repetido
Con n podemos buscar exactemente el numero de repeticiones:
{n} -> exactamente n repetciones
{n,} -> n o más repeticiones
{,m} -> como máximo m repeticiones
{n,m} -> entre n y m repeticiones
Busca el más grande de todos:
str_view(x, "C{2}") # la más larga de 2
str_view(x, "C{2,}") # la más larga a paprtir de 2
str_view(x, "C{2,3}") # a más larga entre dos y 3
# Otros ejemplos para buscar las más cortas:
str_view(x, "C[LX]+?")
Creación de un vector con el que vamos a trabajar. La idea es usar una expresión regular para localizar las frutas que tienen pares repetidos de letras(silaba repetida):
fruits <- c("banana","coco","papaya","manzana","pera","pepino")
str_view(fruits, "(..)\\1", match = TRUE) # la expresión reggular (..) dos cosas cualesquiera que se repitan 1 vez, con match igual a TRUE, que busca solo los que coinciden
Otro ejemplo para expresión regular:
str_view("abc-abc","(...)-\\1") # expresión regular que encuentre exactamente esta sentencia, con -\\1 indicamos que le primer elemento de la expresion regular queremos que vuelva a aparecer
str_view("abc-abc-","(...)(-)\\1\\2") # expresión regular que encuentra exactametne la sentencia previa. Con \\1 decimos que vuelva a encontrar el primer grupo y al segundo grupo
# Si canviásemos el string con la misma expresión regular:
str_view("abc-def-","(...)(-)\\1\\2") # el prime grupo no coincide con el segundo grupo
Otras herramientas:
fruits
## [1] "banana" "coco" "papaya" "manzana" "pera" "pepino"
str_detect(fruits, "a")
## [1] TRUE FALSE TRUE TRUE TRUE FALSE
Por ejemplo, usando el dataset que viene por defecto de words:
head(words)
## [1] "a" "able" "about" "absolute" "accept" "account"
sum(str_detect(words, "^j")) # ¿quántas palabras empiean por j?
## [1] 6
mean(str_detect(words, "^[aeiou]")) # ¿% de palabras que empiezan por vocal?
## [1] 0.1785714
mean(str_detect(words, "[aeiou]$")) # ¿% de palabras que acaban por vocal?
## [1] 0.2765306
Como encontraríamos todas las palabras que no contienen ni una vocal:
f1 <- sum(!str_detect(words, "[aeiou]"))
f1
## [1] 6
# otra forma de hacerlo
f2 <- sum(str_detect(words, "^[^aeiou]+$"))
f2
## [1] 6
# ?son identicas las dos expresiones?:
identical(f1,f2)
## [1] TRUE
String detect nos ayuda para detectar los propios patrones que contienen las palabras:
words[!str_detect(words, "[aeiou]")]
## [1] "by" "dry" "fly" "mrs" "try" "why"
Todas las palabras que acaban en vocal:
head(str_subset(words, "[aeiou]$"))
## [1] "a" "able" "absolute" "achieve" "active" "advertise"
df <- tibble(
words = words,
i = seq_along(word)
)
head(df)
Ver las palabras que acaban en “x” para el dataframe “df”:
df %>% filter(str_detect(words, "x$"))
Podemos hacer uso de la función str_count() que en vez de dar una condición booleana de TRUE or FALSE como str_detect(), cuenta el número de veces que aparece un carácter dentro de un string:
str_count(fruits, "a")
## [1] 3 0 3 3 1 0
O para el dataset de words, ver el promedio de las vocales por palabra:
mean(str_count(words, "[aeiou]"))
## [1] 1.991837
Conclusión, las palabres más comunes en inglés tinene casi dos vocales por palabra.
Esta función funciona muy bien con mutate():
# df %>%
# mutate(
# vowels = str_count(word, "[aeiou]"),
# consonants = str_count(word, "[^aeiou]")
# )
Con esta función se evita el overlaping:
str_count("abababababa", "aba")# debería contar 5, pero conta 3
## [1] 3
str_view_all("abababababa", "aba")
La diferencia entre el str_view() y el str_view_all() es que una busca una ocurrencia, y la otra busca todas las ocurrencias.
Análisis de el array “sentences” dentro del paquete de stringr:
head(sentences)
## [1] "The birch canoe slid on the smooth planks."
## [2] "Glue the sheet to the dark blue background."
## [3] "It's easy to tell the depth of a well."
## [4] "These days a chicken leg is a rare dish."
## [5] "Rice is often served in round bowls."
## [6] "The juice of lemons makes fine punch."
¿Cómo encontrar todas las frases que contengan un determinado color? Primero definimos los colores:
colors <- c("red", "orange", "yellow", "green", "blue", "purple")
color_match <- str_c(colors, collapse = "|") # junta los colores en un nuevo data set separados por una barra vertical
color_match
## [1] "red|orange|yellow|green|blue|purple"
# Filtramos las frases que tengan un determinado color:
has_color <- str_subset(sentences, color_match)
head(has_color)
## [1] "Glue the sheet to the dark blue background."
## [2] "Two blue fish swam in the tank."
## [3] "The colt reared and threw the tall rider."
## [4] "The wide road shimmered in the hot sun."
## [5] "See the cat glaring at the scared mouse."
## [6] "A wisp of cloud hung in the blue air."
# buscar las coincidencias exactas de colores
matches <- str_extract(has_color, color_match)
matches
## [1] "blue" "blue" "red" "red" "red" "blue" "yellow"
## [8] "red" "red" "green" "red" "red" "blue" "red"
## [15] "red" "red" "red" "blue" "red" "blue" "red"
## [22] "green" "red" "red" "red" "red" "red" "red"
## [29] "green" "red" "green" "red" "purple" "green" "red"
## [36] "red" "red" "red" "red" "blue" "red" "blue"
## [43] "red" "red" "red" "red" "green" "green" "green"
## [50] "red" "red" "yellow" "red" "orange" "red" "red"
## [57] "red"
# ?Que colores aparecen mas de una vez?
more_than_one <- sentences[str_count(sentences, color_match) > 1]
str_view_all(more_than_one,color_match)
str_extract_all(more_than_one, color_match) # creación de lista de listas
## [[1]]
## [1] "blue" "red"
##
## [[2]]
## [1] "green" "red"
##
## [[3]]
## [1] "orange" "red"
Como usar str_extract_all():
x <- c("x","x y", "x y z")
str_extract_all(x, "[a-z]", simplify = TRUE) # el parámetro simplify evita crear lista y crea un dataframe en relación a el valore de cara string. Las expresiones van de la a a la z
## [,1] [,2] [,3]
## [1,] "x" "" ""
## [2,] "x" "y" ""
## [3,] "x" "y" "z"
Consideramos que después de una proposición “a” o “the” siempre vendrá un nombre:
noun <- "(a|the) ([^ ]+)" # una palabra para una expresión regular es al menos un carácter que no sea un espacio
# filtramos el conjunto de sustantivo:
sentences %>%
str_subset(noun) %>% # frases que contienen sustantivos
str_extract(noun) %>% # extraemos los sustantivos exactos o completa
head(20)
## [1] "the smooth" "the sheet" "the depth" "a chicken" "the parked"
## [6] "the sun" "the huge" "the ball" "the woman" "a helps"
## [11] "the man's" "the sea." "the booth" "a hole" "the bent"
## [16] "the pants" "the view" "the tank." "the tall" "the same"
Si quisiéramos ver la coincidencia completa:
sentences %>%
str_subset(noun) %>% # frases que contienen sustantivos
str_match(noun) %>% # extraemos los sustantivos con la coincidencia completa, junto con todos los elementos que forman parte de la expresión regular
head(20)
## [,1] [,2] [,3]
## [1,] "the smooth" "the" "smooth"
## [2,] "the sheet" "the" "sheet"
## [3,] "the depth" "the" "depth"
## [4,] "a chicken" "a" "chicken"
## [5,] "the parked" "the" "parked"
## [6,] "the sun" "the" "sun"
## [7,] "the huge" "the" "huge"
## [8,] "the ball" "the" "ball"
## [9,] "the woman" "the" "woman"
## [10,] "a helps" "a" "helps"
## [11,] "the man's" "the" "man's"
## [12,] "the sea." "the" "sea."
## [13,] "the booth" "the" "booth"
## [14,] "a hole" "a" "hole"
## [15,] "the bent" "the" "bent"
## [16,] "the pants" "the" "pants"
## [17,] "the view" "the" "view"
## [18,] "the tank." "the" "tank."
## [19,] "the tall" "the" "tall"
## [20,] "the same" "the" "same"
Si tienes los strings en una tibble:
tibble(sentence = sentences) %>%
tidyr::extract( # extraerá en dos columnas a partir de la expresión regular que se introduce
sentence, c("article","noun"),
"(a|the) ([^ ]+)",
remove = FALSE # no elimines la sentence
)
Recordar: si queréis todas las ocurrencias y no solo las primeras que encuentre dentro del array, podéis hacer uso de str_match_all().
str_replace()
str_replace_all()
str_replace_na() -> reemplaza los NAs
Cómo usar estas funciones:
str_replace(fruits, "[aeiou]", "_")
## [1] "b_nana" "c_co" "p_paya" "m_nzana" "p_ra" "p_pino"
Si queréis reemplazarlas todas:
str_replace_all(fruits, "[aeiou]", "_")
## [1] "b_n_n_" "c_c_" "p_p_y_" "m_nz_n_" "p_r_" "p_p_n_"
Otras opciones de reemplazo haciendo uso de un vector entero:
str_replace_all(c("1 coche", "2 telefonos", "3 amigos"),
c("1" = "un", "2" = "dos", "3" = "tres")
)
## [1] "un coche" "dos telefonos" "tres amigos"
Incercambiar el orden de la segunda y la tercera palabra en una frase:
sentences %>%
str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%
head(10)
## [1] "The canoe birch slid on the smooth planks."
## [2] "Glue sheet the to the dark blue background."
## [3] "It's to easy tell the depth of a well."
## [4] "These a days chicken leg is a rare dish."
## [5] "Rice often is served in round bowls."
## [6] "The of juice lemons makes fine punch."
## [7] "The was box thrown beside the parked truck."
## [8] "The were hogs fed chopped corn and garbage."
## [9] "Four of hours steady work faced us."
## [10] "Large in size stockings is hard to sell."
Dividir una frase en palabras:
sentences %>%
head(3) %>%
str_split(" ") %>%
.[[1]] # seleccionar la posición 1 de la lista. Esto además ayuda a seleccionar los vectores dentro de la lista
## [1] "The" "birch" "canoe" "slid" "on" "the" "smooth"
## [8] "planks."
Podéis también introducir el parámetro simplify = TRUE para generar una matriz:
sentences %>%
head(3) %>%
str_split(" ", n = 3, simplify = TRUE) # con n = 3 nos devolverá las 3 primeras posiciones de cada valore de la lista
## [,1] [,2] [,3]
## [1,] "The" "birch" "canoe slid on the smooth planks."
## [2,] "Glue" "the" "sheet to the dark blue background."
## [3,] "It's" "easy" "to tell the depth of a well."
El anterior caso tiene más sentido para el siguiente ejemplo:
fields <- c("Name: Alejandro Magno", "Country: Greece", "Age: 99")
fields %>% str_split(": ", n = 2, simplify = TRUE)
## [,1] [,2]
## [1,] "Name" "Alejandro Magno"
## [2,] "Country" "Greece"
## [3,] "Age" "99"
Usando la función boundary() acepta divisiones por letra, salto de linea, por frase o por palabra. Cómo romper una frase por palabras:
sent <- "Frase de ejemplo para provar. Y esto es todo."
str_view_all(sent, boundary("word"))
str_split(sent, " ") #Problema: te incluye el punto
## [[1]]
## [1] "Frase" "de" "ejemplo" "para" "provar." "Y" "esto"
## [8] "es" "todo."
Otras funciones para saber las posiciones de inicio y de fin dentro de la frase:
str_locate
str_locate_all
Ejemplos:
str_locate_all(sent, "[aeiou] ")
## [[1]]
## start end
## [1,] 5 6
## [2,] 8 9
## [3,] 16 17
## [4,] 21 22
## [5,] 36 37
Generamos un sub string a partir de un string:
str_sub(sent, 8, 9) # información de la posición
## [1] "e "
Ejemplos para trabajar con parámetros:
apples <- c("manzana","Manzana","MANZANA")
str_view(apples, "manzana") # busca la palabra manzana exactamente
str_view(apples, regex("manzana", ignore_case = TRUE)) # dentro del parámetro regex podemos determinar mas parámetros, como que ignore las mayúsculas y minúsculas
Otros ejemplos:
x <- "Linea 1\nLinea 2\nLinea 3\nLinea 4"
str_extract_all(x, "^Linea")[[1]] #[[]] para que salga en formato vector, pero solo aparece 1, ¿cómo podemos hacer para que aparezcan todas?
## [1] "Linea"
str_extract_all(x, regex("^Linea", multiline = TRUE))[[1]] # localizara las 4 "linea" el multiline se encarga de localizar el inicio y final de linea
## [1] "Linea" "Linea" "Linea" "Linea"
Ejemplo expresión regular para saber si es un teléfono:
phone <- regex("
\\(? # paréntesis de oportura opcionales
(\\d{3}) # código de área (ej: barcelona 34)
[)-]? # cierre de paréntesis, guión o espacio opcionales
(\\d{3}) # tres dígitos de teléfono
[ -]? # espacio o guión opcional
(\\d{3}) # tres digitos finales
", comments = TRUE)
str_match("971-123-456", phone)
## [,1] [,2] [,3] [,4]
## [1,] "971-123-456" "971" "123" "456"
Después hay otro parámetro, dotall=TRUE, que permite que el “.” reemplaze cualquier cosa. Cargamos de la librería microbenchmark
microbenchmark::microbenchmark(
regex = str_detect(sentences, "the"),
fixed = str_detect(sentences, fixed("the")),
times = 30
)
Funciones útiles del paquete base:
apropos() -> busca variables en el envoirement a partir de ciertos valores, y te devuelve valores aproximados
dir() -> ficheros de un directorio, puedes buscar patrones dentro de él para encontrar algún fichero en concreto con el parámetro pattern=“.xls” por ejemplo para buscar ficheros excel.
Usaremos el paquete “forecats”
library(tidyverse)
library(forcats)
Creación de una serie de factores:
day_levels <- c("Lun","Mar","Mie","Jue","Vie","Sab","Dom")
x1 <- c("Vie","Lun","Mar","Dom")
sort(x1)
## [1] "Dom" "Lun" "Mar" "Vie"
# Con la creación de la variable y1 usando el orden establecido day_levels, podemos ordenar de forma correcta los factores.
y1 <- factor(x1, day_levels)
sort(y1)
## [1] Lun Mar Vie Dom
## Levels: Lun Mar Mie Jue Vie Sab Dom
Imaginemos que nos equivoquemos al introducir unos de los factores:
x2 <- c("Vim","Lun","Mar","Dom")
y2 <- factor(x2, levels = day_levels)
y2
## [1] <NA> Lun Mar Dom
## Levels: Lun Mar Mie Jue Vie Sab Dom
El resultado nos da NA para el valor incorrecto. Si se quiere crear un warning, podemos usar un parse:
y2 <- parse_factor(x2, levels = day_levels)
## Warning: 1 parsing failure.
## row # A tibble: 1 x 4 col row col expected actual expected <int> <int> <chr> <chr> actual 1 1 NA value in level set Vim
Podemos crear un factor a partir de los valores únicos, ordenados por orden de aparición:
f1 <- factor(x1, levels = unique(x1))
x1 %>% factor() %>% fct_inorder()
## [1] Vie Lun Mar Dom
## Levels: Vie Lun Mar Dom
Usaremos el dataset gss_cat que es una muestra de factores ya preparados el la librería forcats:
head(gss_cat)
Ver alguna información relevante de este dataset:
gss_cat %>% count(marital)
gss_cat %>% ggplot(aes(marital))+
geom_bar()+
scale_x_discrete(drop = FALSE)
Hipótesis: ¿la religión tiene relacion con las horas que se ve la televisión?
religion_summary <- gss_cat %>%
group_by(relig) %>%
summarise(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n()
)
ggplot(religion_summary, aes(tvhours,relig))+
geom_point()
# Este grafico no nos muestra muy bien los patrones. Vamos a ordenar los factores:
ggplot(religion_summary,aes(tvhours, fct_reorder(relig, tvhours)))+
geom_point()
La función fct_reorder() ordena de menor a mayor a partir de la mediana. Es útil usarlo solo por los factores que estan ordenados de forma arbitraria. Podemos hacer fuera de ggplot, con un mutate(), y ademas quedará mejor dentro del grafico:
religion_summary %>%
mutate(relig = fct_reorder(relig, tvhours)) %>%
ggplot(aes(tvhours, relig))+
geom_point()
Otra hipótesis: ¿Hay relación entre ingresos y horas de televisión?
gss_cat %>%
group_by(rincome) %>%
summarise(age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n()
) %>%
mutate(rincome = fct_reorder(rincome, age)) %>%
ggplot(aes(age, rincome))+
geom_point()
Sin embargo, puede que sea mejor elegir nosotros mismos cuál es el factor que queremos que vaya el último de la fila, esto lo haremos con fct_relevel():
gss_cat %>%
group_by(rincome) %>%
summarise(age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n()
) %>%
mutate(rincome = fct_relevel(rincome, "Not applicable")) %>%
ggplot(aes(age, rincome))+
geom_point()
El fct_reorder2() reordena el facotor de variables y al valor más alto de x. Ejemplo:
by_age <- gss_cat %>%
filter(!is.na(age)) %>%
group_by(age,marital) %>%
count()
ggplot(by_age, aes(age, n, color = marital))+
geom_line(na.rm = TRUE)
¿Cómo hacerlo con el fct_reorder2()?
ggplot(by_age, aes(age, n, color = fct_reorder2(marital, age, n)))+
geom_line(na.rm = TRUE)+
labs(color = "Marital")
Para los bar_plots estan los factors inflects, para reeordenarlo de forma decreciente:
gss_cat %>%
mutate(marital = marital %>% fct_infreq() %>% fct_rev()) %>%
ggplot(aes(marital)) +
geom_bar()
Usar la función fct_recode() que nos permite modificar o cambiar los niveles de un factor determinado.
gss_cat %>%
count(partyid)
Vamos a ver si podemos reedefinir estos niveles para establecer una construcción totalemte paralela:
gss_cat %>%
mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat",
"Other" = "No answer",
"Other" = "Don't know",
"Other" = "Other party"
)) %>% count(partyid)
Si se quieren colapsar muchos más niveles, puedes usar la función colapse():
gss_cat %>%
mutate(partyid = fct_collapse(partyid,
other = c("No answer","Don't know", "Other party"),
rep = c("Strong republican", "Not str republican"),
ind = c("Ind,near rep","Independent","Ind,near dem"),
dem = c("Not str democrat","Strong democrat")
)
) %>% count(partyid)
O si se quiere juntar pequenos grupos para hacer el dataframe más simple, se puede hacer uso de fct_lump(), que juntara los factores más y menos comunos en niveles comunes:
gss_cat %>%
mutate(relig = fct_lump(relig, n = 5)) %>% # con n determinamos el nombre de grupos
count(relig, sort = TRUE) %>%
print(n = 3) # imprimir en pantalla solo los 3 primeros
## # A tibble: 6 x 2
## relig n
## <fct> <int>
## 1 Protestant 10846
## 2 Catholic 5124
## 3 None 3523
## # ... with 3 more rows
Usaremos el paquete lubridate
library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
##
## date
library(nycflights13)
Existen 3 tipos de fechas/horas:
Funciones útiles;
today()
## [1] "2018-11-22"
now()
## [1] "2018-11-22 12:03:34 CET"
Creación de datetimes a partir de strings:
ymd("2015-06-13")
## [1] "2015-06-13"
mdy("Enero 30th, 2018")
## [1] "2018-01-30"
dmy("8-Jun-2018")
## [1] "2018-06-08"
ymd(20180608)
## [1] "2018-06-08"
Si queréis añadir tiempo como si fuera un string, deberá seguir otro formato para parsear la función correctamente:
ymd_hms("2018-06-08 19:53:18")
## [1] "2018-06-08 19:53:18 UTC"
mdy_hm("06/20/2018 05:30", tz = "GMT") # se a?ade la zona est?ndar europea
## [1] "2018-06-20 05:30:00 GMT"
Ejemplo de uso y transformación de fechas:
# Ejemplo de creación de la variable departure:
flights %>%
select(year, month, day, hour, minute) %>%
mutate(departure = make_datetime(year, month, day, hour, minute)) %>%
head(5)
# Creamos una función para unificar la información en una sola variable:
make_date_time_100 <- function(year, month, day, time){
make_datetime(year, month, day, time%/% 100, time %% 100)
}
# Creamos un nuevo fichero donde añadiremos las nuevas variables:
flights_dt <- flights %>%
filter(!is.na(dep_time), !is.na(arr_time)) %>%
mutate(
dep_time = make_date_time_100(year, month, day, dep_time),
arr_time = make_date_time_100(year, month, day, arr_time),
sched_dep_time = make_date_time_100(year, month, day, sched_dep_time),
sched_arr_time = make_date_time_100(year, month, day, sched_arr_time)
) %>% select(origin, dest, ends_with("delay"), ends_with("time"))
# Representación gráfica de las horas de salida:
flights_dt %>%
ggplot(aes(dep_time))+
geom_freqpoly(binwidth = 24*60*60)
En el caso de querer generar fechas a partir de otros datos, lo dejor es usar:
as_datetime(today())
## [1] "2018-11-22 UTC"
as_date(now())
## [1] "2018-11-22"
Ejemplos de como sacar mes, día o año a una fecha determinada:
ahora <- now()
# La fecha
year(ahora) # con label y abbreviate podemos ponerlo en formato string y abreviaro
## [1] 2018
month(ahora) # con label y abbreviate podemos ponerlo en formato string y abreviaro
## [1] 11
mday(ahora) # día del mes
## [1] 22
yday(ahora) # día del año
## [1] 326
wday(ahora) # día de la semana, que empieza por domingo
## [1] 5
# El tiempo
hour(ahora)
## [1] 12
minute(ahora)
## [1] 3
second(ahora)
## [1] 36.56229
¿Qué día de la semana hay más vuelos? Cogemos el dataset préviamente creeado:
flights_dt %>%
mutate(wday = wday(dep_time, label = TRUE, abbr = FALSE)) %>%
ggplot(aes(x = wday)) +
geom_bar()
# El domingo la gente vuelve de vacaciones
¿La gran mayoría de vuelos se retrasan menos si salen a y media o a en punto?
flights_dt %>%
mutate(minute = minute(dep_time)) %>%
group_by(minute) %>%
summarise(
avg_delay = mean(arr_delay, na.rm = TRUE),
n = n()
)%>%
ggplot(aes(minute, avg_delay))+
geom_line()
# Parece ser que la hipótesis no iba desencaminada
¿Hay la misma cantidad de vuelos programados segun si es y media o en punto o no?
flights_dt %>%
mutate(minute = minute(sched_dep_time)) %>%
group_by(minute) %>%
summarise(
avg_delay = mean(arr_delay, na.rm = TRUE),
n = n()
)%>%
ggplot(aes(minute, avg_delay))+
geom_line()
# Parece ser que no tienen en cuenta el patrón de delays cada media hora y en punto
¿Es posible que horas de salida esté programadas a horas “bonitas”, exactas?
flights_dt %>%
mutate(minute = minute(sched_dep_time)) %>%
group_by(minute) %>%
summarise(
avg_delay = mean(arr_delay, na.rm = TRUE),
n = n()
)%>%
ggplot(aes(minute, n))+
geom_line()
# Hipótesis correcta, siempre estan programdos a i 5, 10 i 30.... La gran mayoría de vuelos estan programados a en punto o y media.
Las funciones que usaremos serán:
floor_date()
round_date()
ceiling_date()
Vemos algunos ejemplos:
# ¿cuál es el departure time por semana?
flights_dt %>%
count(week = floor_date(dep_time, "week")) %>% # hemos redondeado a la baja
ggplot(aes(week, n)) +
geom_line()
A veces nos va interesar cambiar alguna fecha, se puede hacer con lubridate(). Ejemplo:
# Asignamos a la variable del momento actual
d <- now()
d
## [1] "2018-11-22 12:03:39 CET"
# modificamos el año al 2030
year(d) <- 2030
# modificamos el mes al mes de febrero
month(d) <- 02
d
## [1] "2030-02-22 12:03:39 CET"
# modificamos la hora y les sumamos 3 horas respecto a la hora actual
hour(d) <- hour(d) + 3
d
## [1] "2030-02-22 15:03:39 CET"
También se puede modificar la fecha creando una nueva a partir de la original con la función update():
update(d, year = 2020, month = 10, mday = 7, hour = 4)
## [1] "2020-10-07 04:03:39 CEST"
d
## [1] "2030-02-22 15:03:39 CET"
¿Qué pasa si cae en un día que no existe, por ejemplo un 30 de febrero? Pues que es capaz de predecirlo:
dmy("01-02-2018") %>%
update(mday = 30)
## [1] "2018-03-02"
Ejemplos de uso de update() con el fichero fligths, como si fuera una serie temporal.
flights_dt %>%
mutate(dep_hour = update(dep_time, yday = 1)) %>%
ggplot(aes(dep_hour))+
geom_freqpoly(binwidth = 300)
Con esta funciones hemos puesto todos los valores al 1 de enero. Esto nos ayuda a localizar patrones entre todos los vuelos. Es un análisis de series temporales.
Nos ayudarán a entender cómo funciona la aritmética de tiempo, que operaciones se puede realizar. ¿Qué se debe tener en cuenta?
# diferencia de días entre el valor de todat() y el 1988-05-19
value <- today() - ymd(19880519)
value
## Time difference of 11144 days
# el objeto difftime permite guardar un lapso de tiempo
class(value)
## [1] "difftime"
lubridate puede transformarlo a segundos para hacerlo más fácil:
as.duration(value)
## [1] "962841600s (~30.51 years)"
Podemos establecer duraciones de segundos a partir de segundos, minutos… :
dseconds(3245)
## [1] "3245s (~54.08 minutes)"
dminutes(2341)
## [1] "140460s (~1.63 days)"
dweeks(4)
## [1] "2419200s (~4 weeks)"
dyears(1)
## [1] "31536000s (~52.14 weeks)"
Se puede usar la duración para crear vectores:
dhours(c(12,24))
## [1] "43200s (~12 hours)" "86400s (~1 days)"
ddays(5:10)
## [1] "432000s (~5 days)" "518400s (~6 days)" "604800s (~1 weeks)"
## [4] "691200s (~1.14 weeks)" "777600s (~1.29 weeks)" "864000s (~1.43 weeks)"
Esta forma fácil que nos permite lubridate nos ayuda a hacer cálculos muy senzillos:
2*dyears(1)
## [1] "63072000s (~2 years)"
dyears(1) + dweeks(13) + dhours(22)
## [1] "39477600s (~1.25 years)"
tomorrow <- today() + ddays(1)
tomorrow
## [1] "2018-11-23"
last_year <- today() - dyears(1)
last_year
## [1] "2017-11-22"
En el caso que sumemos 1 día a la 1 del mediodía del 12 de marzo:
one_pm <- ymd_hms("2016-03-12 13:00:00", tz = "America/New_York")
one_pm
## [1] "2016-03-12 13:00:00 EST"
# si le sumamos un dia nos da 2 horas mas de las que deberia ser
one_pm + ddays(1)
## [1] "2016-03-13 14:00:00 EDT"
El motivo es que hay un cambio de zona horaria, una es EST la otra es EDT. Ese día es el cambio que hora, por lo tanto, el 12 de marzo solo tiene 23 horas. ¿Cómo soluciona este problema? Lubridate te permite acceder a los periodos que ayudan a tratar este problema.
Esta forma de acceder a les fechas con lubridate, trabaja con percepción de tiempo “humana”, como días, meses o años. Ejemplo de la suma de un día cuando lo usamos en periodos:
one_pm + days(1)
## [1] "2016-03-13 13:00:00 EDT"
Las funciones que se pueden usar son las mismas que para duraciones() pero sin la “d” delante. Ejemplos:
seconds(35)
## [1] "35S"
minutes(15)
## [1] "15M 0S"
hours(c(12,24))
## [1] "12H 0M 0S" "24H 0M 0S"
days(7)
## [1] "7d 0H 0M 0S"
weeks(4)
## [1] "28d 0H 0M 0S"
months(1:6)
## [1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S"
## [5] "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
years(1)
## [1] "1y 0m 0d 0H 0M 0S"
Se pueden realizar operaciones igual que con las duraciones:
10+months(6) + days(8)
## [1] "6m 8d 0H 0M 10S"
days(20) + hours(32) + minutes(45)
## [1] "20d 32H 45M 0S"
ymd("2016-07-31") + years(2)
## [1] "2018-07-31"
Ejemplo del dataset de vuelos. El hecho que hayan algunos vuelos que hayan salido de algunos aeropuertos con distinta zona horaria que el aeropuerto de llegada, podrían causar que aviones lleguen antes que la hora de salida respecto al lugar de llegada. ¿Sería esto posibe?
# Esto deberia estar vacio, nadie puede llegar antes de que salga:
flights_dt %>%
filter(arr_time < dep_time) %>%
head(5)
Podemos ver que sí que hay resultados, ¿cómo es posible? El problema es que cuando hemos generado las fechas no lo hemos hecho de forma correcta:
flights_dt%>%
mutate(overnight = arr_time > dep_time,
arr_time = arr_time + days(overnight*1),
sched_arr_time = sched_arr_time + days(overnight*1)
) -> flights_dt
# después de la modificación sí que debería funcionar
flights_dt %>%
filter(overnight, arr_time < dep_time)
Ahora sí que podemos ver que nos da un buen resultado, dado que ningun vuelo puede llegar antes de la hora que haya salido.
Un ejemplo con flights para explicar este caso. Si yo divido 1 año por 365, debería darme 1:
dyears(1)/ddays(365)
## [1] 1
Sin embargo si haces la misma división por 1 año con lubridate por periodos:
years(1)/days(1)
## estimate only: convert to intervals for accuracy
## [1] 365.25
Nos da errores. ¿Cómo solucionarlo?
next_year <- today() + years(1)
(today() %--% next_year) %/% days(1)
## Note: method with signature 'Timespan#Timespan' chosen for function '%/%',
## target signature 'Interval#Period'.
## "Interval#ANY", "ANY#Period" would also be valid
## [1] 365
Resumen para elegrr las opciones de lubridate:
Hay muchos problemas con los nombres que se le dan. R usa el estándard internacional de IANA. Funciona de la siguiente forma:
Se introducen estos valores con este orden. Ejemplo:
Europe/Madrid
Europe/Paris
America/New_york
Se usa continente porque una ciudad puede pasar de un país a otro, de continente, no.
Para saber en que horario está vuestro sistema puedes hacer:
Sys.timezone()
## [1] "Europe/Paris"
Si quieres saber quantas zonas horarias hay:
length(OlsonNames())
## [1] 592
Ejemplos de horas a variables segun zonas horarias:
x1 <- ymd_hms("2018-06-08 12:00:00", tz = "America/New_York")
x2 <- ymd_hms("2018-06-08 18:00:00", tz = "Europe/Copenhagen")
x3 <- ymd_hms("2018-06-09 04:00:00", tz = "Pacific/Auckland")
x1 - x2 # Da 0 porque es la misma hora
## Time difference of 0 secs
x2 - x3 # también da 0
## Time difference of 0 secs
Lubridate siempre eligirá el tiempo coordenado universal (UTC que es lo mismo que el GMT). Si combinamos las 3 horas anteriores, el propio R lo internacionalizará:
c(x1,x2,x3)
## [1] "2018-06-08 12:00:00 EDT" "2018-06-08 12:00:00 EDT"
## [3] "2018-06-08 12:00:00 EDT"
R cogera el valor medio de NY,USA.