class: center, middle, inverse, title-slide <div class="title-logo"></div> # Análisis de Datos ## Tema 3 - Data Wrangling ### 3.3 Organización de Datos <br> <br> .pull-left[ ### Roi Naveiro ] --- ## Data Wrangling **Objetivo**: dejar los datos listos para su posterior **exploración** y **modelización** Convertir **datos crudos** en **datos procesados** **Datos crudos** - Los datos tal cual aparecen en la fuente de origen - No han sufrido ninguna manipulación **Datos procesados** - Cada variable es una columna - Cada observación una fila - Cada unidad observacional es una celda - Datos más complejos, en varias tablas interconectadas --- ## Data Wrangling * Importación de los datos * Organización de los datos * Transformación de los datos <img src="img/data-science-wrangle.png" width="100%" style="display: block; margin: auto;" /> --- class: center, middle, inverse # Transformación de datos --- ## Transformación de datos * Strings * Factores * Fechas y horas --- class: center, middle, inverse # Strings --- ## Strings * Veremos lo básico de manipulación de strings * Datos en formato texto ```r "Esto es un string" ``` ``` ## [1] "Esto es un string" ``` ```r c("una", "dos y tres") ``` ``` ## [1] "una" "dos y tres" ``` * Expresiones regulares (regexps): permiten identificar patrones en texto * Usaremos `stringr` de tidyverse para manipular strings * Todas sus funciones empiezan con `str_` --- ## Strings: longitud Podemos calcular la longitud de una string usando `str_length()` ```r str_length("Hola") ``` ``` ## [1] 4 ``` Las operaciones están vectorizadas ```r str_length( c("Hola", "Adiós") ) ``` ``` ## [1] 4 5 ``` **Pregunta:** ¿Qué pasa si hay espacios? --- ## Strings: combinación Combinamos strings con `str_c` ```r str_c("Ma", "drid") ``` ``` ## [1] "Madrid" ``` ```r str_c("Un", "dos", sep=",") ``` ``` ## [1] "Un,dos" ``` **Pregunta**: ¿Cómo combinarías dos strings con una coma seguida de un espacio? --- ## Strings: combinación También vectorizado! ```r str_c("prefijo-", c("X", "Y", "Z"), "-sufijo") ``` ``` ## [1] "prefijo-X-sufijo" "prefijo-Y-sufijo" "prefijo-Z-sufijo" ``` Para colapsar un texto largo en una única string ```r str_c(c("En", "un", "lugar", "de", "la", "Mancha"), collapse = " ") ``` ``` ## [1] "En un lugar de la Mancha" ``` --- ## Strings: subsetting Para extraer partes de una string usamos `str_sub` ```r x <- c("Liberty", "La Nuit") str_sub(x, 1, 3) ``` ``` ## [1] "Lib" "La " ``` ```r # Con números negativos empieza desde el final str_sub(x, -3, -1) ``` ``` ## [1] "rty" "uit" ``` --- ## Strings: cosas útiles ```r x <- c("Liberty", "La nuit") str_to_lower(x) ``` ``` ## [1] "liberty" "la nuit" ``` ```r str_to_upper(x) ``` ``` ## [1] "LIBERTY" "LA NUIT" ``` ```r str_to_title(x) ``` ``` ## [1] "Liberty" "La Nuit" ``` --- ## Strings: expresiones regulares Permiten describir patrones en strings. Se pueden hacer varias cosas con los patrones * Determinar que strings cumplen el patrón. * Determinar en qué posición aparece el patrón * Extraer el patrón * Reemplazarlo E.g. patrón "contiene ES seguido de dos números" ```r x1 <- "ES49" x2 <- "Mi número de cuenta es ES35 30..." x3 <- "Resido en España (ES)" ``` --- ## Strings: expresiones regulares Vamos aprender a jugar con patrones muy sencillos. Primero, coincidencia exacta (ojo mayúsculas y minúsculas!) ```r x <- c("Cats", "Uñas Chung Lee", "Capital") str_view(x, "Le") ```
**Pregunta**: diferencia entre `str_view` y `str_view_all` --- ## Strings: expresiones regulares Para identificar cualquier caracter usamos `.` ```r x <- c("Cats", "Uñas Chung Lee", "Capital") str_view(x, ".a.") ```
--- ## Strings: expresiones regulares Empieza por `^` y termina por `$` ```r x <- c("Cats", "Uñas Chung Lee", "Capital") str_view(x, "^C") ```
```r str_view(x, "l$") ```
--- ## Strings: expresiones regulares `\d`: cualquie dígito `\s`: espacio en blanco `[abc]`: a, b, o c. ```r x = c("B12", "Eslava", "Ocho y Medio") str_view_all(x, "\\d") ```
OJO ```r writeLines("\\d") ``` ``` ## \d ``` --- ## Strings: expresiones regulares ```r x = c("ES12", "Mi cuenta es ES46", "Resido en España (ES)") str_view(x, "ES\\d\\d") ```
--- ## Strings: expresiones regulares **Ejercicio**: Detecta las autovías correctas ```r x <- c("A1", "A12", "A14", "A6", "EA5", "A3", "F2") ``` --- ## Strings: detectar patrones Con esto estamos preparados para detectar patrones ```r x = c("ES12", "Mi cuenta es ES46", "Resido en España (ES)") str_detect(x, "ES\\d\\d") ``` ``` ## [1] TRUE TRUE FALSE ``` Uso común, seleccionar los elementos que cumplen el patrón ```r words[str_detect(words, "x$")] ``` ``` ## [1] "box" "sex" "six" "tax" ``` --- ## Strings: detectar patrones También con dataframes ```r df <- tibble( word = words, i = seq_along(word) ) df %>% filter(str_detect(word, "x$")) ``` ``` ## # A tibble: 4 × 2 ## word i ## <chr> <int> ## 1 box 108 ## 2 sex 747 ## 3 six 772 ## 4 tax 841 ``` --- ## Strings: contar patrones ¿Cuántas veces ocurre el patrón? ```r x <- c("apple", "banana", "pear") str_count(x, "a") ``` ``` ## [1] 1 3 1 ``` **Ejercicio**: calcula la media de vocales por palabra en `words`. --- ## Strings: contar patrones Se usa mucho con mutate ```r df %>% mutate( vocales = str_count(word, "[aeiou]"), ) ``` ``` ## # A tibble: 980 × 3 ## word i vocales ## <chr> <int> <int> ## 1 a 1 1 ## 2 able 2 2 ## 3 about 3 3 ## 4 absolute 4 4 ## 5 accept 5 2 ## 6 account 6 3 ## 7 achieve 7 4 ## 8 across 8 2 ## 9 act 9 1 ## 10 active 10 3 ## # … with 970 more rows ``` --- class: center, middle, inverse # Factores --- ## Factores * Para trabajar con variables categóricas con número fijo de valores * Trabajaremos con `forcats` de tiduverse * Crear factores --- ## Factores: creación ```r x1 <- c("Dec", "Apr", "Jan", "Mar") ``` Usar una string para almacenar esta variable tiene dos problemas * Los valores son fijos y nada previene de errores ```r x1 <- c("Dec", "Apr", "Jan", "Mer") ``` * No ordena de manera útil ```r sort(x1) ``` ``` ## [1] "Apr" "Dec" "Jan" "Mer" ``` --- ## Factores: creación Esto se arregla creando un factor ```r month_levels <- c( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ) y1 <- factor(x1, levels = month_levels) sort(y1) ``` ``` ## [1] Jan Apr Dec ## Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ``` --- ## Factores: creación Con pipes ```r f2 <- x1 %>% factor(levels = month_levels) f2 ``` ``` ## [1] Dec Apr Jan <NA> ## Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ``` --- ## Factores Trabajaremos con los datos `gss_cat` ```r glimpse(gss_cat) ``` ``` ## Rows: 21,483 ## Columns: 9 ## $ year <int> 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 20… ## $ marital <fct> Never married, Divorced, Widowed, Never married, Divorced, Mar… ## $ age <int> 26, 48, 67, 39, 25, 25, 36, 44, 44, 47, 53, 52, 52, 51, 52, 40… ## $ race <fct> White, White, White, White, White, White, White, White, White,… ## $ rincome <fct> $8000 to 9999, $8000 to 9999, Not applicable, Not applicable, … ## $ partyid <fct> "Ind,near rep", "Not str republican", "Independent", "Ind,near… ## $ relig <fct> Protestant, Protestant, Protestant, Orthodox-christian, None, … ## $ denom <fct> "Southern baptist", "Baptist-dk which", "No denomination", "No… ## $ tvhours <int> 12, NA, 2, 4, 1, NA, 3, NA, 0, 3, 2, NA, 1, NA, 1, 7, NA, 3, 3… ``` --- ## Factores **Pregunta**: ¿Cómo identificarías los niveles posibles de la variable `race`? -- ```r gss_cat %>% count(race) ``` ``` ## # A tibble: 3 × 2 ## race n ## <fct> <int> ## 1 Other 1959 ## 2 Black 3129 ## 3 White 16395 ``` --- ## Factores * Reordenar niveles * Modificar niveles --- ## Factores - Reordenar niveles Con `fct_reorder()`. Toma tres argumentos * `f`, el factor que queremos reordenar * `x`, vector numérico para ordenar * Opcionalmente, `fun`, en caso de que haya varios valores de `x` para la misma `f`. (Por defecto, `fun` es la mediana) --- ## Factores - Reordenar niveles Tres opciones ```r gss_cat %>% group_by(relig) %>% summarise(tvhours = mean(tvhours, na.rm = TRUE)) %>% ggplot(aes(tvhours, fct_reorder(relig, tvhours))) + geom_point() + theme_bw() ``` ![](tema3-3_files/figure-html/unnamed-chunk-30-1.png)<!-- --> --- ## Factores - Reordenar niveles Tres opciones ```r gss_cat %>% group_by(relig) %>% summarise(tvhours = mean(tvhours, na.rm = TRUE)) %>% mutate (relig = fct_reorder(relig, tvhours)) %>% ggplot(aes(tvhours, relig)) + geom_point() + theme_bw() ``` ![](tema3-3_files/figure-html/unnamed-chunk-31-1.png)<!-- --> --- ## Factores - Reordenar niveles Tres opciones ```r relig_summary <- gss_cat %>% group_by(relig) %>% summarise( age = mean(age, na.rm = TRUE), tvhours = mean(tvhours, na.rm = TRUE), n = n() ) ggplot(relig_summary, aes(tvhours, relig)) + geom_point() + theme_bw() ``` ![](tema3-3_files/figure-html/unnamed-chunk-32-1.png)<!-- --> --- ## Factores - Modificar niveles Usamos `fct_recode()` ```r gss_cat %>% count(partyid) ``` ``` ## # A tibble: 10 × 2 ## partyid n ## <fct> <int> ## 1 No answer 154 ## 2 Don't know 1 ## 3 Other party 393 ## 4 Strong republican 2314 ## 5 Not str republican 3032 ## 6 Ind,near rep 1791 ## 7 Independent 4119 ## 8 Ind,near dem 2499 ## 9 Not str democrat 3690 ## 10 Strong democrat 3490 ``` --- ## Factores - Modificar niveles ```r 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" )) %>% count(partyid) ``` ``` ## # A tibble: 10 × 2 ## partyid n ## <fct> <int> ## 1 No answer 154 ## 2 Don't know 1 ## 3 Other party 393 ## 4 Republican, strong 2314 ## 5 Republican, weak 3032 ## 6 Independent, near rep 1791 ## 7 Independent 4119 ## 8 Independent, near dem 2499 ## 9 Democrat, weak 3690 ## 10 Democrat, strong 3490 ``` --- ## Factores - Modificar niveles A veces útil colapsar niveles ```r 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) ``` ``` ## # A tibble: 4 × 2 ## partyid n ## <fct> <int> ## 1 other 548 ## 2 rep 5346 ## 3 ind 8409 ## 4 dem 7180 ``` --- class: center, middle, inverse # Fechas y horas --- ## Fechas y horas Para trabajar con fechas y horas usamos `lubridate` que **NO** es parte tidyverse. Descárgalo y cárgalo en R ```r library(lubridate) ``` --- ## Fechas y horas Tres tipos * Fechas, referidas como <date> en los tibble * Hora, referida como <time> en los tibble * Fecha y hora, identifica unívocamente un tiempo. Referidas como <dttm> en los tibble (en R, POSIXct) --- ## Fechas y horas ```r today() ``` ``` ## [1] "2022-11-13" ``` ```r str(today()) ``` ``` ## Date[1:1], format: "2022-11-13" ``` ```r now() ``` ``` ## [1] "2022-11-13 20:46:44 CET" ``` ```r str(now()) ``` ``` ## POSIXct[1:1], format: "2022-11-13 20:46:44" ``` --- ## Fechas y horas En general proceden de strings. **Pregunta**: ¿Cómo convertir strings en objetos de fecha o fecha-hora? --- ## Fechas y horas * Otra forma de parsear más fácil con funciones de lubridate * Solo especificar orden usando “y”, “m”, y “d” ```r ymd("2017-01-31") ``` ``` ## [1] "2017-01-31" ``` ```r ymd("17-Jan-31") ``` ``` ## [1] "2017-01-31" ``` --- ## Fechas y horas ```r mdy("January 31st, 2017") ``` ``` ## [1] "2017-01-31" ``` ```r dmy("31 of January of 2017") ``` ``` ## [1] "2017-01-31" ``` ```r ymd_hms("2017-01-31 20:11:59") ``` ``` ## [1] "2017-01-31 20:11:59 UTC" ``` ```r mdy_hm("01/31/2017 08:01") ``` ``` ## [1] "2017-01-31 08:01:00 UTC" ``` --- ## Fechas y horas A veces nos dan las componentes individuales por separado ```r library(nycflights13) flights %>% select(year, month, day, hour, minute) ``` ``` ## # A tibble: 336,776 × 5 ## year month day hour minute ## <int> <int> <int> <dbl> <dbl> ## 1 2013 1 1 5 15 ## 2 2013 1 1 5 29 ## 3 2013 1 1 5 40 ## 4 2013 1 1 5 45 ## 5 2013 1 1 6 0 ## 6 2013 1 1 5 58 ## 7 2013 1 1 6 0 ## 8 2013 1 1 6 0 ## 9 2013 1 1 6 0 ## 10 2013 1 1 6 0 ## # … with 336,766 more rows ``` --- ## Fechas y horas Para esto `make_datetime()`. Ojo orden de argumentos!! ```r flights %>% select(year, month, day, hour, minute) %>% mutate(departure = make_datetime(year, month, day, hour, minute)) ``` ``` ## # A tibble: 336,776 × 6 ## year month day hour minute departure ## <int> <int> <int> <dbl> <dbl> <dttm> ## 1 2013 1 1 5 15 2013-01-01 05:15:00 ## 2 2013 1 1 5 29 2013-01-01 05:29:00 ## 3 2013 1 1 5 40 2013-01-01 05:40:00 ## 4 2013 1 1 5 45 2013-01-01 05:45:00 ## 5 2013 1 1 6 0 2013-01-01 06:00:00 ## 6 2013 1 1 5 58 2013-01-01 05:58:00 ## 7 2013 1 1 6 0 2013-01-01 06:00:00 ## 8 2013 1 1 6 0 2013-01-01 06:00:00 ## 9 2013 1 1 6 0 2013-01-01 06:00:00 ## 10 2013 1 1 6 0 2013-01-01 06:00:00 ## # … with 336,766 more rows ``` --- ## Fechas y horas Cambiar entre formatos ```r as_datetime(today()) ``` ``` ## [1] "2022-11-13 UTC" ``` ```r as_date(now()) ``` ``` ## [1] "2022-11-13" ``` --- ## Fechas y horas ¿Qué hacer con fechas y horas? * Extraer componentes * Realizar operaciones --- ## Fechas y horas - Componentes ```r datetime <- ymd_hms("2016-07-08 12:34:56") year(datetime) ``` ``` ## [1] 2016 ``` ```r month(datetime) ``` ``` ## [1] 7 ``` ```r mday(datetime) ``` ``` ## [1] 8 ``` ```r yday(datetime) ``` ``` ## [1] 190 ``` ```r wday(datetime) ``` ``` ## [1] 6 ``` --- ## Fechas y horas - Componentes ```r datetime <- ymd_hms("2016-07-08 12:34:56") hour(datetime) ``` ``` ## [1] 12 ``` ```r minute(datetime) ``` ``` ## [1] 34 ``` ```r second(datetime) ``` ``` ## [1] 56 ``` --- ## Fechas y horas - Componentes ```r datetime <- ymd_hms("2016-07-08 12:34:56") month(datetime, label = TRUE) ``` ``` ## [1] Jul ## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec ``` ```r wday(datetime, label = TRUE, abbr = FALSE) ``` ``` ## [1] Friday ## 7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday ``` --- ## Fechas y horas - Componentes Podemo modificar componentes usando las mismas funciones ```r (datetime <- ymd_hms("2016-07-08 12:34:56")) ``` ``` ## [1] "2016-07-08 12:34:56 UTC" ``` ```r year(datetime) <- 2020 datetime ``` ``` ## [1] "2020-07-08 12:34:56 UTC" ``` ```r month(datetime) <- 01 datetime ``` ``` ## [1] "2020-01-08 12:34:56 UTC" ``` ```r hour(datetime) <- hour(datetime) + 1 datetime ``` ``` ## [1] "2020-01-08 13:34:56 UTC" ``` --- ## Fechas y horas - Componentes También podemos modificar componentes con `update` ```r update(datetime, year = 2023) ``` ``` ## [1] "2023-01-08 13:34:56 UTC" ``` --- ## Fechas y horas - Operaciones Para operar con fechas, necesitaremos: * **Duraciones**: número exacto de segundos * **Períodos**: otras unidades como meses y semanas --- ## Fechas y horas - Operaciones Al restar dos fechas, obtenemos un objeto de clase `difftime` ```r mi_edad <- today() - ymd("1993 May 17") mi_edad ``` ``` ## Time difference of 10772 days ``` Este objeto alberga tiempos en segundos, minutos, horas, días, etc. Ambiguo --- ## Fechas y horas - Operaciones con duraciones Alternativa, trabajar con **duraciones** (en segundos) ```r as.duration(mi_edad) ``` ``` ## [1] "930700800s (~29.49 years)" ``` --- ## Fechas y horas - Operaciones con duraciones Funciones úliles. Siempre en segundos!! ```r dseconds(15) ``` ``` ## [1] "15s" ``` ```r dminutes(10) ``` ``` ## [1] "600s (~10 minutes)" ``` ```r dhours(c(12, 24)) ``` ``` ## [1] "43200s (~12 hours)" "86400s (~1 days)" ``` ```r ddays(0:5) ``` ``` ## [1] "0s" "86400s (~1 days)" "172800s (~2 days)" ## [4] "259200s (~3 days)" "345600s (~4 days)" "432000s (~5 days)" ``` --- ## Fechas y horas - Operaciones con duraciones Funciones úliles. Siempre en segundos!! ```r dweeks(3) ``` ``` ## [1] "1814400s (~3 weeks)" ``` ```r dyears(1) ``` ``` ## [1] "31557600s (~1 years)" ``` --- ## Fechas y horas - Operaciones con duraciones ```r dyears(1) + dweeks(12) + dhours(15) ``` ``` ## [1] "38869200s (~1.23 years)" ``` ```r 2*dweeks(12) ``` ``` ## [1] "14515200s (~24 weeks)" ``` ```r mañana <- today() + ddays(1) año_pasado <- today() - dyears(1) ``` --- ## Fechas y horas - Operaciones con períodos Más intuitivo ```r seconds(15) ``` ``` ## [1] "15S" ``` ```r minutes(10) ``` ``` ## [1] "10M 0S" ``` ```r hours(c(12, 24)) ``` ``` ## [1] "12H 0M 0S" "24H 0M 0S" ``` ```r days(7) ``` ``` ## [1] "7d 0H 0M 0S" ``` --- ## Fechas y horas - Operaciones con períodos Más intuitivo ```r 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" ``` ```r weeks(3) ``` ``` ## [1] "21d 0H 0M 0S" ``` ```r years(1) ``` ``` ## [1] "1y 0m 0d 0H 0M 0S" ``` --- ## Fechas y horas - Operaciones con períodos Operaciones con fechas ```r ymd("2017-01-01") + years(1) ``` ``` ## [1] "2018-01-01" ``` ```r ymd("2017-01-01") + months(1) ``` ``` ## [1] "2017-02-01" ``` ```r ymd("2017-01-01") + days(1) ``` ``` ## [1] "2017-01-02" ``` --- ## Fechas y horas - Operaciones con períodos ¿Qué está pasando? ```r ymd("2016-01-01") + dyears(1) ``` ``` ## [1] "2016-12-31 06:00:00 UTC" ``` ```r ymd("2016-01-01") + years(1) ``` ``` ## [1] "2017-01-01" ``` --- ## Bibliografía Este tema está fundamentalmente basado en [R for Data Science](https://r4ds.had.co.nz/), Wickham and Grolemund (2016)