Hay tres partes del data wrangling:
Importación
Limmpieza ¿Cómo podemos saber si un dato esta limpio o no?
Transformación para crear visualizaciones y modelos
Para hacer este proceso haremos uso de la herramiento TIBBLE de Tidyverse.
Diferentes tipos de datos para el data wrangling:
datos relacionales
strings: texto escrito
factores: datos categóricos, finitos y limitado.
fechas: day & time
Un tibble es una versión especial de un dataframe para tidyverse. Para ver toda la información de un tibble puedes escribir en consola: vignette(“tibble”)
La mayoria de funciones con tidyverse aceptan tibbles, pero no las funciones que estan fuera del paquete tidyverse no aceptan este formato mejorado de los dataframes. Ejemplo de tibble y data.frame:
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()
class(iris)
## [1] "data.frame"
Si queremos convertir el data.frame iris a una estructura tibble, lo haremos:
iris_tibble <- as_tibble(iris)
class(iris_tibble)
## [1] "tbl_df" "tbl" "data.frame"
¿Cómo los podemos crear? Claro, con tibble():
t <- tibble(
x = 1:10,
y = pi, # repetira automaticamente 10 "pi"
z = pi * x ^ 2
)
t
Una tibble nunca cambia el tipo de dato de una variable a diferencia de un data frame. No cambia los nombres de las variables sin motivo aparente. Tampoco crea nombres a la filas.
El acceso a las tibbles es lo mismo que con los dataframes.
También podemos dar nombres “no sintáctico” a las variables. Por ejemplo que empezara con espacio o un emoticono. Ejemplo:
t2 <- tibble(
':)' = "smile", # los ' son los backsticks
' ' = "space",
'1988' = "number"
)
t2
También se pueden crear tribble(), que se utiliza para transponer los datos. Se definen las cabezeras con fórmulas.
tribble(
~x,~y,~z,
#----/-/----/
"a",4,3.14,
"b",8,6.28,
"c",9,-1.25
)
¿Qué diferencias hay entre un tibble y un dataframe clásico?
Vamos a crear un tibble para ver cómo se puede trabajar con él:
library(lubridate)
##
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
##
## date
t <- tibble(
a = lubridate::now() + runif(1e3)*24*60, # generar horas aleatorias
b = 1:1e3,
c = lubridate::today() + runif(1e3)+30,
d = runif(1e3),
e = sample(letters, 1e3, replace = T)
)
head(t)
Por consola solo saldrían 10 resultados.
Si ponemos options() en la consola, se podrían modificar los parámetros por defecto de las tibbles. Por ejemplo, si quiseramos que como máximo imprimiera por pantalla 12 filas, y como mínimo 8 filas:
options(tibble.print_max = 12, tibble.print_min=8)
[…]
Funciones del paquete readr:
read_csv() -> ficheros separados por “,”
read_csv2() -> ficheros separados por “;”
read_tsv() -> separados por el tabulador " " o “”
read_delim() -> separado por el delimitador que tu creas. Ejemplo: read_delim(delim = ‘n.’)
read_fwf() -> fichero ‘fixed with file’, todas y cada una de las filas tiene la misma anchura y el mismo número de carácteres. Se debe especificar la anchura por donde se debe cortar dentro de la función fwf_widths() y fwf_positions()
read_table -> busca en espacio en blanco entre valores de cada linea
read_log() -> ficheros creados por apache para leer errores de un servidor (install.packages(“webreadr”))
Nos centraremos en read_csv.
Usaremos el siguiente código para guardar los datos:
¿Cómo cargar el archivo?:
cars <- read_csv("cars.csv") # en els warnings nos advierte que la primera columna no tiene nombre de variable y se le ha asignado X1
## Warning: Missing column names filled in: 'X1' [1]
## Parsed with column specification:
## cols(
## X1 = col_character(),
## mpg = col_double(),
## cyl = col_integer(),
## disp = col_double(),
## hp = col_integer(),
## drat = col_double(),
## wt = col_double(),
## qsec = col_double(),
## vs = col_integer(),
## am = col_integer(),
## gear = col_integer(),
## carb = col_integer()
## )
Cuando hagamos una importación con readr se nos mostrará el formato de las variables como información.
También podemos creear tibbles con la función readr:
read_csv("x,y,z
1,2,3
4,5,6
7,8,9")
Metadato: descripción de la tabla, de donde ha sido sacado…, información general. Para evitar que read lea la parte informativa al inicio, podemos usar el parametro skip() el número de lineas que quieres evitar:
read_csv("texto con informacion del
archivo interesante
x,y,z
1,2,3
4,5,6", skip = 2)
si tenemos columnas con #, es decir, con comentarios, tenemos que indicarlo como parametro, para que cada vez que encuentre un # se lo salte.
read_csv("#Esto es un comentario
x,y,z
1,2,3
4,5,6", comment = "#")
Si queremos creear un tibble sin nombre de las columnas (las generara R automáticamente)
read_csv("1,2,3\n4,5,6\n7,8,9", col_names = FALSE)
Si queremos añadir las columnas como un vector:
read_csv("1,2,3\n4,5,6\n7,8,9", col_names = c("primera","segunda","tercera"))
¿Cómo tratar los NA o valores desconocidos al cargar el archivo?:
read_csv("x,y,z\n1,2,.\n4,#,6", na = c(".","#"))
Las funciones que empiezan por parse_[…]. Estas funciones no cogen un vector cualquiera, te devuelve el vector procesado de vectores lógicos. Te permite tranformar cualquier tipo de text en el tipo de dato que corresponde.
str(parse_logical(c("TRUE", "FALSE", "FALSE","NA")))
## logi [1:4] TRUE FALSE FALSE NA
Por números:
str(parse_integer(c("1", "2", "3","4")))
## int [1:4] 1 2 3 4
Por fechas:
str(parse_date(c("1988-05-19", "2018-05-12")))
## Date[1:2], format: "1988-05-19" "2018-05-12"
Es decir, parse_[cualquier tipo de dato] nos servirá para procesar toda la información necesaria.
También podemos especificar si hay algun carácter que debe ser NA.
str(parse_integer(c("1", "2", "#","5"), na = "#"))
## int [1:4] 1 2 NA 5
¿Qué pasa cuando tenemos números enteros con otros valores que no comparten su naturaleza?
data <- parse_integer(c("1", "2","hola","5","3.141592"))
## Warning in rbind(names(probs), probs_f): number of columns of result is not
## a multiple of vector length (arg 1)
## Warning: 2 parsing failures.
## row # A tibble: 2 x 4 col row col expected actual expected <int> <int> <chr> <chr> actual 1 3 NA an integer hola row 2 5 NA no trailing characters .141592
problems(data)
En este caso usamos la función problems para localizar los problemas, como podemos ver en la posición 3 y 5 del vector.
¿Qué tipos tenemos de procesadores de tipos de datos?
parse_logical()
parse_integer()
parse_double() -> procesa valores SOLO numéricos
parse_number() -> es un poco más flexible que el parse_double()
Tenemos que tener en cuenta que en diferentes puntos del mundo se escriben de forma distinta. Por ejemplo pueden tener un carácter monetario a su lado. Reunimos todos los casos que pueden variar:
parse_double("12.345") # funcionará de forma correcta
## [1] 12.345
parse_double("12,345", locale = locale(decimal_mark = ",")) # no funcionará si no se especifíca el separador que da problemas
## [1] 12.345
monetarios 100???, 56$
porcentajes 12%
# funciona perfectamente:
parse_number("100???")
## [1] 100
parse_number("???100")
## [1] 100
parse_number("12%")
## [1] 12
parse_number("Cuesta unos 45???")
## [1] 45
parse_number("$1,000,000") # funciona perfectamente en este caso en concreto
## [1] 1e+06
parse_number("1.000.000", locale = locale(grouping_mark = "."))
## [1] 1e+06
parse_number("123'456'789", locale = locale(grouping_mark = "'"))
## [1] 123456789
¿Cómo se representa la información intermitentemente en los ordenadores?
charToRaw("JoanClaverolRomero") #usa el formato ASCII para que sean leidos para el ordenador
## [1] 4a 6f 61 6e 43 6c 61 76 65 72 6f 6c 52 6f 6d 65 72 6f
Las codificaciones más clásicas para la correcta interpretación de los carácteres:
Latin1 (ISO-8859-1) para idiomas de Europa del Oeste
Latin2 (ISO-8859-2) para idiomas de Europa del Oeste
Hoy día ya existe un formato que puede codificar cualquier idioma del mundo:
Este sistema lo usa readr, entoces podría dar problemas si hicieramos frente a dataset codificados con un sistema antiguo. Ejemplo de errores que podemos tener:
x1 <- "El Ni\xf1o ha estado enfermo"
parse_character(x1,locale = locale(encoding = "Latin1"))
## [1] "El Niño ha estado enfermo"
Otro caso sería:
x2 <- "\x82\xb1\x82\xf1\x82\xb2\x82\xcd"
parse_character(x2, locale = locale(encoding = "Shift-JIS"))
## [1] "<U+3053><U+3093><U+3054><U+306F>"
En muchos casos no sabremos como está codificado. En este caso podemos usar la función guess_encoding():
guess_encoding(charToRaw(x1))
O en el otro caso:
guess_encoding(charToRaw(x2))
Contra más carácteres tenga, podrá hacer una predicción más buena sobre la codificacion que usa.
months <- c("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")
parse_factor(c("May","Jun","Jul","Aug","Sep","Oct","Nov"), levels = months)
## [1] May Jun Jul Aug Sep Oct Nov
## Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Número de días desde que han pasado desde la epoca EPOCH, 1 de enero de 1970, origen de las fechas de la informática.
parse_datetime() ISO-8601 va del mayor valor al menor year/month/day/hour/minute/second
parse_ date() -> el formato que necesita:
# Los dos funcionan de forma correcta
parse_date("2015-12-07")
## [1] "2015-12-07"
parse_date("2017/05/18")
## [1] "2017-05-18"
# Funcionan correctamente
parse_time("03:00 pm")
## 15:00:00
parse_time("20:00:34")
## 20:00:34
Interpretación del tiempo segun R:
Años:
%Y -> año con 4 dígitos
%y -> año con 2 dígitos (00-69) -> 2000-2069 // (70-99) -> 1970-1999
Meses:
%m -> mes en formato de dos digitos de 01-12
%b -> abreviación del mes "Ene","Feb"
%B -> nombre completo del mes "Enero","Febrero",...
Día:
%d -> número del día con dos digitos 01-31
%e -> de formato opcional, los dígitos 1-9 pueden llevar espacio en blanco
Horas:
%H -> hora entre 0-23
%I -> hora entre 0-12 siempre va con %p
%p -> am/pm
%M -> minutos 0-59
%s -> segundos enteros 0-59
%OS -> segundos reales
%Z -> zona horaria America/Chicago, Canadá, France, Spain
%z -> zona horaria con respecto a la UTC (zona horaria internacional)
Carácteres especiales de no dígitos
%. -> eliminar un carácter no dígito
%* -> eliminar cualquier número de carácteres que no sean dígitos
Ejemplos:
parse_date("05/08/15", format = "%d/%m/%y")
## [1] "2015-08-05"
parse_date("08/05/15", format = "%m/%d/%y")
## [1] "2015-08-05"
parse_date("01-05-2018", format = "%d-%m-%Y")
## [1] "2018-05-01"
parse_date("01 Jan 2018", format = "%d %b %Y")
## [1] "2018-01-01"
parse_date("03 March 17", format = "%d %B %y")
## [1] "2017-03-03"
parse_date("5 janvier 2012", format = "%d %B %Y", locale = locale("fr"))
## [1] "2012-01-05"
parse_date("3 Septiembre 2014", format = "%d %B %Y", locale = locale("es"))
## [1] "2014-09-03"
Readr usa la heurística (probabilidad) para ver que tipo de dato tiene cada columna. El orden que sigui es el siguiente:
Veamos un ejemplo donde podamos definir el tipo de variable para cada columna:
challenge <- read_csv(
readr_example("challenge.csv"),
col_types = cols(
x = col_double(), # parse double
y = col_date() # parse_date()
)
)
Podemos también hacer uso de la función stop_for_problems(), que parara la ejecución de la carga de un archivo si encuentra algún problema.
Vamos a ver que otros problemas podemos evitar en la carga del archivo:
challenge3 <- read_csv(readr_example("challenge.csv"),
col_types = cols(.default = col_character()))
Usamos la función type_convert():
head(type.convert(challenge3)) # la y debería ser una "date"
Formas de guardar los data frames tratados:
write_csv()
write_tsv()
Siempre se deben guardar los strings en UTF8 para eviar problemas de codificación
Guardar siempre los date y/o datetimes en ISO08601
Para importarlo especialmente a excel csv, como:
Ejemplo como guardarlo:
write_csv(challenge, path = "challenge.csv")
Cuando se realiza un guardado en csv, se pierden las características de cada variable. Para hacer que lo cargue de forma correcta:
challenge <- read_csv("challenge.csv", guess_max = 1001)
## Parsed with column specification:
## cols(
## x = col_double(),
## y = col_date(format = "")
## )
head(challenge)
Podemos usar los ficheros rds para conservar todos los datos generados con R:
write_rds(challenge, path = "challenge.rds")
Ahora podemos cargarlo y se mantienen la información tratada:
head(read_rds("challenge.rds")) # mirar el formato de y que es date
Podemos hacer uso también de las siguientes librerías:
paquete haven, que sirve para trabajar con SPSS, Stata y SAS
readxl -> .xml, .xmls
DBI -> RMySQL, RSQLite, RPostreSQL
jsonlite
xml2