La limpieza de datos (en inglés data cleansing o data scrubbing) es el acto de descubrimiento y corrección o eliminación de registros de datos erróneos de una tabla o base de datos. El proceso de limpieza de datos permite identificar datos incompletos, incorrectos, inexactos, no pertinentes, etc. y luego substituir, modificar o eliminar estos datos sucios (“data duty”). Después de la limpieza, la base de datos podrá ser compatible con otras bases de datos similares en el sistema.
La limpieza de datos se diferencia de la validación de datos, que casi siempre cumple la función de rechazar los registros erróneos durante la entrada al sistema y no en lotes de data. El proceso de limpieza de datos incluye la validación y además la corrección de datos, para alcanzar datos de calidad.
La calidad de datos debe cumplir con los siguientes requisitos:
Los análisis y algoritmos son solo el reflejo de los datos en los que se basan. En promedio, las organizaciones creen que casi el 30 % de sus datos son inexactos. Estos datos erróneos cuestan a las empresas el 12 % de sus ingresos generales, aunque están perdiendo mucho más que solo dinero. La limpieza produce datos coherentes, estructurados y exactos, lo que permite tomar decisiones fundamentadas e inteligentes. También permite destacar las áreas de mejora en los entornos de almacenamiento y entrada de datos de flujo ascendente, lo que ahorra tiempo y dinero tanto ahora como en el futuro.(fuente: alteryx.com)
Data cleansing o limpieza de datos es un paso esencial para cualquier proceso analítico y normalmente incluye seis pasos:
ETAPAS
Deduplicar: los duplicados generalmente aparecen cuando se combinan datos de diferentes fuentes (p. ej., hojas de cálculo, sitios web y bases de datos) o cuando un cliente tiene varios puntos de contacto con una empresa o ha enviado formularios redundantes. Estos datos repetidos consumen espacio en el servidor y recursos de procesamiento, lo que crea archivos más grandes y un análisis menos eficaz. Las reglas sobre la deduplicación dependen del resultado esperado de la empresa. Por ejemplo, si un cliente envió diferentes correos electrónicos en distintas páginas de un sitio web, un enfoque prudente eliminaría solo filas de datos en las que cada campo es una coincidencia exacta.
Quitar observaciones irrelevantes: los datos que no son relevantes para el problema que se resuelve pueden ralentizar el tiempo de procesamiento. Quitar estas observaciones irrelevantes no las elimina de la fuente, sino que las excluye del análisis actual. Por ejemplo, cuando se investigan campañas del último año, no es necesario incluir datos que estén fuera de ese período. Sin embargo, hay que tener en cuenta que incluso si una determinada variable no es necesaria, esta podría estar correlacionada con el resultado que se investiga (por ejemplo, la edad de un cliente podría incidir en qué correo electrónico tuvo más éxito).
Administrar datos incompletos: es posible que falten valores en los datos por algunas razones (por ejemplo, que los clientes no proporcionen cierta información), y ocuparse de esto es fundamental para el análisis, ya que evita los sesgos y los cálculos erróneos. Después de aislar y examinar los valores incompletos, que pueden aparecer como “0”, “NA”, “ninguno”, “nulo” o “no aplicable”, determine si estos son valores plausibles o si se deben a la falta de información. Si bien la solución más sencilla puede ser descartar los datos incompletos, hay que tener en cuenta el sesgo que puede suponer esa acción. Las alternativas incluyen reemplazar los valores nulos con sustitutos basados en modelos estadísticos o condicionales, o marcar y comentar los datos que faltan.
Identificar valores atípicos: los puntos de datos que están muy alejados del resto de la población pueden distorsionar significativamente la realidad de los datos. Estos valores atípicos se pueden identificar con técnicas visuales o numéricas, como los diagramas de caja, los histogramas, los diagramas de dispersión o las puntuaciones Z; cuando forman parte de un proceso automatizado, permiten hacer suposiciones rápidas, comprobar esas suposiciones y resolver los problemas de los datos con confianza. Una vez identificados, los valores atípicos se pueden incluir u omitir en función de lo extremos que sean y de los métodos estadísticos que se utilicen en un análisis.
Corregir errores estructurales: es importante corregir errores e inconsistencias, lo que incluye tipografías, uso de mayúsculas, abreviaturas y formato. Observa el tipo de dato de cada columna y asegúrate de que las entradas sean correctas y coherentes, lo que puede incluir la estandarización de campos, y quita los caracteres no deseados, como los espacios en blanco adicionales.
Validar: la validación es la oportunidad de garantizar que los datos sean exactos, completos, coherentes y uniformes. Esto ocurre en todo el proceso de limpieza de datos automatizado, pero sigue siendo importante realizar una muestra para asegurarse de que todo está alineado. Esta también es una oportunidad para documentar qué herramientas y técnicas se utilizaron en el proceso de limpieza.
Podemos adicionar:
Hay muchas herramientas de limpieza de datos como:
Optimus Optimus es el framework faltante para la limpieza, el pre-procesamiento y el análisis exploratorio de datos de una manera distribuida con Apache Spark. documentación
Amazon SageMaker Amazon SageMaker Data Wrangler es una característica de Amazon SageMaker que le permite preparar datos de forma rápida y fácil para el ML
Para uso en Python: se recomienda el uso de librarías como Pandas
Para uso en R: se recomienda la librería Dplyr
dplyr es una gramática de manipulación de datos que proporciona un conjunto consistente de verbos que resuelven los desafíos de manipulación de datos más comunes.
La manera más simple, es instalar todo el paquete, de la siguiente manera:
install.packages(“tidyverse”) library(tidyverse)
o directamente:
install.packages(“dplyr”) library(dplyr)
Para usar desde la versión en desarrollo, se puede instalar desde Github:
install.packages(“devtools”) devtools::install_github(“tidyverse/dplyr”)
Al trabajar con datos debe:
El paquete dplyr hace que estos pasos sean rápidos y fáciles. Conjunto básico de herramientas de dplyr y cómo aplicarlas a marcos de datos.
Para explorar las funciones básicas de dyplyr, usaremos el dataset
starwars
. El data set cotiene filas de características
sobre los personales de la película. Tomados de: Star Wars API, mas informacón en
?starwars
dim(starwars)
#> [1] 87 14
starwars
#> # A tibble: 87 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 Darth Vader 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
starwars
es un tibble, una reinvención moderna del marco
de datos. Es particularmente útil para grandes conjuntos de datos porque
solo imprime las primeras filas. Más sobre tibbles en https://tibble.tidyverse.org; en particular, puede
convertir marcos de datos en tibbles con as_tibble()
.
dplyr tiene como objetivo proporcionar una función para cada verbo básico de manipulación de datos. Estos verbos se pueden organizar en tres categorías según el componente del conjunto de datos con el que trabajan:
Filas: + filter()
elige filas en
función de los valores de las columnas. + slice()
elige
filas según la ubicación. + arrange()
cambia el orden de
las filas.
Columnas:
select()
cambia si se incluye o no una columna.rename()
cambia el nombre de las columnas.mutate()
cambia los valores de las columnas y crea
nuevas columnas.relocate()
cambia el orden de las columnas.Grupos de filas: + summarise()
colapsa
un grupo en una sola fila.
Todas las funciones de dplyr toman un marco de datos (o tibble) como
primer argumento. En lugar de obligar al usuario a guardar objetos
intermedios o anidar funciones, dplyr proporciona el operador
%>%
de magrittr. x %>% f(y)
se convierte
en f(x, y)
, por lo que el resultado de un paso se
“transmite” al siguiente paso. Puede usar pipa para reescribir varias
operaciones que puede leer de izquierda a derecha y de arriba a
abajo.
filter()
filter()
permite seleccionar un subconjunto de filas en
un marco de datos. Como todos los verbos individuales, el primer
argumento es el tibble (o marco de datos). El segundo argumento y los
subsiguientes se refieren a variables dentro de ese marco de datos,
seleccionando filas donde la expresión es VERDADERO
.
Por ejemplo, podemos seleccionar todos los personajes con color de piel claro y ojos marrones con:
starwars %>% filter(skin_color == "light", eye_color == "brown")
#> # A tibble: 7 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Leia Organa 150 49 brown light brown 19 fema… femin… Aldera…
#> 2 Biggs Darkl… 183 84 black light brown 24 male mascu… Tatooi…
#> 3 Cordé 157 NA brown light brown NA fema… femin… Naboo
#> 4 Dormé 165 NA brown light brown NA fema… femin… Naboo
#> # … with 3 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
Es equivalente a decirle a R:
starwars[starwars$skin_color == "light" & starwars$eye_color == "brown", ]
arrange()
arrange()
funciona de manera similar a
filter()
excepto que en lugar de filtrar o seleccionar
filas, las reordena. Se necesita un marco de datos y un conjunto de
nombres de columna (o expresiones más complicadas) para ordenar. Si
proporciona más de un nombre de columna, cada columna adicional se usará
para desempatar los valores de las columnas anteriores:
starwars %>% arrange(height, mass)
#> # A tibble: 87 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Yoda 66 17 white green brown 896 male mascu… <NA>
#> 2 Ratts Tyere… 79 15 none grey, … unknown NA male mascu… Aleen …
#> 3 Wicket Syst… 88 20 brown brown brown 8 male mascu… Endor
#> 4 Dud Bolt 94 45 none blue, … yellow NA male mascu… Vulpter
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
Usamos desc()
para ordenar de forma descendente:
starwars %>% arrange(desc(height))
#> # A tibble: 87 × 14
#> name height mass hair_c…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Yarael Poof 264 NA none white yellow NA male mascu… Quermia
#> 2 Tarfful 234 136 brown brown blue NA male mascu… Kashyy…
#> 3 Lama Su 229 88 none grey black NA male mascu… Kamino
#> 4 Chewbacca 228 112 brown unknown blue 200 male mascu… Kashyy…
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
slice()
slice()
permite indexar filas por sus ubicaciones
(enteras). Le permite seleccionar, eliminar y duplicar filas.
Podemos obtener caracteres de los números de fila 5 a 10.
starwars %>% slice(5:10)
#> # A tibble: 6 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Leia Organa 150 49 brown light brown 19 fema… femin… Aldera…
#> 2 Owen Lars 178 120 brown,… light blue 52 male mascu… Tatooi…
#> 3 Beru Whites… 165 75 brown light blue 47 fema… femin… Tatooi…
#> 4 R5-D4 97 32 <NA> white,… red NA none mascu… Tatooi…
#> # … with 2 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
Se acompaña de una serie de ayudantes para casos de uso comunes:
slice_head()
y slice_tail()
selecciona las
primeros o ultimas filas.starwars %>% slice_head(n = 3)
#> # A tibble: 3 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> # … with 4 more variables: species <chr>, films <list>, vehicles <list>,
#> # starships <list>, and abbreviated variable names ¹hair_color, ²skin_color,
#> # ³eye_color, ⁴birth_year, ⁵homeworld
slice_sample()
selecciona de manera azarosa. Podemos
usar prop. para determinar proporción.starwars %>% slice_sample(n = 5)
#> # A tibble: 5 × 14
#> name height mass hair_color skin_c…¹ eye_c…² birth…³ sex gender homew…⁴
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Dud Bolt 94 45 none blue, g… yellow NA male mascu… Vulpter
#> 2 Bossk 190 113 none green red 53 male mascu… Trando…
#> 3 Shaak Ti 178 57 none red, bl… black NA fema… femin… Shili
#> 4 Dormé 165 NA brown light brown NA fema… femin… Naboo
#> # … with 1 more row, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹skin_color, ²eye_color, ³birth_year, ⁴homeworld
starwars %>% slice_sample(prop = 0.1)
#> # A tibble: 8 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Qui-Gon Jinn 193 89 brown fair blue 92 male mascu… <NA>
#> 2 Dexter Jett… 198 102 none brown yellow NA male mascu… Ojom
#> 3 R4-P17 96 NA none silver… red, b… NA none femin… <NA>
#> 4 Lama Su 229 88 none grey black NA male mascu… Kamino
#> # … with 4 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
Usamos replace = TRUE
para preparar una muestra. Si
necesitamos, podemos darle peso con el argumento
weight
.
slice_min()
y slice_max()
selecciona filas
con valores altos y bajos. Nota: primero se debe elegir valores que no
sean NA.starwars %>%
filter(!is.na(height)) %>%
slice_max(height, n = 3)
#> # A tibble: 3 × 14
#> name height mass hair_c…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Yarael Poof 264 NA none white yellow NA male mascu… Quermia
#> 2 Tarfful 234 136 brown brown blue NA male mascu… Kashyy…
#> 3 Lama Su 229 88 none grey black NA male mascu… Kamino
#> # … with 4 more variables: species <chr>, films <list>, vehicles <list>,
#> # starships <list>, and abbreviated variable names ¹hair_color, ²skin_color,
#> # ³eye_color, ⁴birth_year, ⁵homeworld
select()
A menudo, trabaja con grandes conjuntos de datos con muchas columnas,
pero solo unas pocas son realmente de mucho interés.
select()
permite tomar rápidamente un subconjunto útil
usando operaciones que generalmente solo funcionan en posiciones de
variables numéricas:
# Seleccion de columnas con nombre
starwars %>% select(hair_color, skin_color, eye_color)
#> # A tibble: 87 × 3
#> hair_color skin_color eye_color
#> <chr> <chr> <chr>
#> 1 blond fair blue
#> 2 <NA> gold yellow
#> 3 <NA> white, blue red
#> 4 none white yellow
#> # … with 83 more rows
# Selecciona toda las columnas entre hair_color y eye_color (inclusive)
starwars %>% select(hair_color:eye_color)
#> # A tibble: 87 × 3
#> hair_color skin_color eye_color
#> <chr> <chr> <chr>
#> 1 blond fair blue
#> 2 <NA> gold yellow
#> 3 <NA> white, blue red
#> 4 none white yellow
#> # … with 83 more rows
# Selecciona todas las columnas excepto las de hair_color y eye_color (inclusive)
starwars %>% select(!(hair_color:eye_color))
#> # A tibble: 87 × 11
#> name height mass birth…¹ sex gender homew…² species films vehic…³ stars…⁴
#> <chr> <int> <dbl> <dbl> <chr> <chr> <chr> <chr> <lis> <list> <list>
#> 1 Luke … 172 77 19 male mascu… Tatooi… Human <chr> <chr> <chr>
#> 2 C-3PO 167 75 112 none mascu… Tatooi… Droid <chr> <chr> <chr>
#> 3 R2-D2 96 32 33 none mascu… Naboo Droid <chr> <chr> <chr>
#> 4 Darth… 202 136 41.9 male mascu… Tatooi… Human <chr> <chr> <chr>
#> # … with 83 more rows, and abbreviated variable names ¹birth_year, ²homeworld,
#> # ³vehicles, ⁴starships
# Select todas las columnas que terminen con color.
starwars %>% select(ends_with("color"))
#> # A tibble: 87 × 3
#> hair_color skin_color eye_color
#> <chr> <chr> <chr>
#> 1 blond fair blue
#> 2 <NA> gold yellow
#> 3 <NA> white, blue red
#> 4 none white yellow
#> # … with 83 more rows
Hay una serie de funciones auxiliares que puede usar dentro de
select()
, como starts_with()
,
ends_with()
, matches()
y
contains()
. Estos permiten hacer coincidir rápidamente
bloques más grandes de variables que cumplen algún criterio. Ver
?select
para más detalles.
Se puede cambiar el nombre de las variables con select()
para eso usamos argumentos con nombre:
starwars %>% select(home_world = homeworld)
#> # A tibble: 87 × 1
#> home_world
#> <chr>
#> 1 Tatooine
#> 2 Tatooine
#> 3 Naboo
#> 4 Tatooine
#> # … with 83 more rows
Pero debido a que select()
elimina todas las variables
que no se mencionan explícitamente, no es tan útil. En su lugar, usa
rename()
:
starwars %>% rename(home_world = homeworld)
#> # A tibble: 87 × 14
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender home_…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 Darth Vader 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵home_world
mutate()
Además de seleccionar conjuntos de columnas existentes, a menudo es
útil agregar nuevas columnas que son funciones de columnas existentes.
Este es el trabajo de mutate()
:
starwars %>% mutate(height_m = height / 100)
#> # A tibble: 87 × 15
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 Darth Vader 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 5 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, height_m <dbl>, and abbreviated variable
#> # names ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
No podemos ver la altura en metros que acabamos de calcular, pero podemos arreglar eso usando un comando de selección.
starwars %>%
mutate(height_m = height / 100) %>%
select(height_m, height, everything())
#> # A tibble: 87 × 15
#> height_m height name mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender
#> <dbl> <int> <chr> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
#> 1 1.72 172 Luke Skywa… 77 blond fair blue 19 male mascu…
#> 2 1.67 167 C-3PO 75 <NA> gold yellow 112 none mascu…
#> 3 0.96 96 R2-D2 32 <NA> white,… red 33 none mascu…
#> 4 2.02 202 Darth Vader 136 none white yellow 41.9 male mascu…
#> # … with 83 more rows, 5 more variables: homeworld <chr>, species <chr>,
#> # films <list>, vehicles <list>, starships <list>, and abbreviated variable
#> # names ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year
dplyr::mutate()
es similar a la base
transform()
, pero le permite hacer referencia a las
columnas que acaba de crear:
starwars %>%
mutate(
height_m = height / 100,
BMI = mass / (height_m^2)
) %>%
select(BMI, everything())
#> # A tibble: 87 × 16
#> BMI name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <dbl> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 26.0 Luke … 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 26.9 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 34.7 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 33.3 Darth… 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 5 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, height_m <dbl>, and abbreviated variable
#> # names ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
Si solo desea mantener las nuevas variables, use
.keep = "none"
:
starwars %>%
mutate(
height_m = height / 100,
BMI = mass / (height_m^2),
.keep = "none"
)
#> # A tibble: 87 × 2
#> height_m BMI
#> <dbl> <dbl>
#> 1 1.72 26.0
#> 2 1.67 26.9
#> 3 0.96 34.7
#> 4 2.02 33.3
#> # … with 83 more rows
relocate()
Podemos usar una sintaxis similar a select()
para mover
bloques de columnas a la vez
starwars %>% relocate(sex:homeworld, .before = height)
#> # A tibble: 87 × 14
#> name sex gender homew…¹ height mass hair_…² skin_…³ eye_c…⁴ birth…⁵
#> <chr> <chr> <chr> <chr> <int> <dbl> <chr> <chr> <chr> <dbl>
#> 1 Luke Skywal… male mascu… Tatooi… 172 77 blond fair blue 19
#> 2 C-3PO none mascu… Tatooi… 167 75 <NA> gold yellow 112
#> 3 R2-D2 none mascu… Naboo 96 32 <NA> white,… red 33
#> 4 Darth Vader male mascu… Tatooi… 202 136 none white yellow 41.9
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹homeworld, ²hair_color, ³skin_color, ⁴eye_color, ⁵birth_year
summarise()
El último verbo es summarise()
. Contrae un marco de
datos en una sola fila.
starwars %>% summarise(height = mean(height, na.rm = TRUE))
#> # A tibble: 1 × 1
#> height
#> <dbl>
#> 1 174.
No es tan útil hasta que aprendemos el verbo group_by()
a continuación.
Es posible que hayas notado que la sintaxis y la función de todos estos verbos son muy similares:
El primer argumento es un marco de datos.
Los argumentos posteriores describen qué hacer con el marco de
datos. Puede refiérase a las columnas en el marco de datos directamente
sin usar $
.
El resultado es un nuevo marco de datos
Juntas, estas propiedades hacen que sea fácil encadenar varios pasos simples para lograr un resultado complejo.
Estas cinco funciones proporcionan la base de un lenguaje de
manipulación de datos. En el nivel más básico, solo puede modificar un
marco de datos ordenado de cinco formas útiles: puede reordenar las
filas (arrange()
), seleccionar observaciones y variables de
interés (filter()
y select()
), agregar nuevas
variables que son funciones de variables existentes
(mutate()
) o colapsar muchos valores en un resumen
(summarise()
).
%>%
La API de dplyr es funcional en el sentido de que las llamadas a funciones no tienen efectos secundarios. Siempre debe guardar sus resultados. Esto no conduce a un código particularmente elegante, especialmente si desea realizar muchas operaciones a la vez. O se tiene que realizar paso a paso:
a1 <- group_by(starwars, species, sex)
a2 <- select(a1, height, mass)
a3 <- summarise(a2,
height = mean(height, na.rm = TRUE),
mass = mean(mass, na.rm = TRUE)
)
Si no se desean nombrar los resultados intermedios, se debe envolver las llamadas de función una dentro de la otra:
summarise(
select(
group_by(starwars, species, sex),
height, mass
),
height = mean(height, na.rm = TRUE),
mass = mean(mass, na.rm = TRUE)
)
#> # A tibble: 41 × 4
#> # Groups: species [38]
#> species sex height mass
#> <chr> <chr> <dbl> <dbl>
#> 1 Aleena male 79 15
#> 2 Besalisk male 198 102
#> 3 Cerean male 198 82
#> 4 Chagrian male 196 NaN
#> # … with 37 more rows
Esto es difícil de leer porque el orden de las operaciones es de
adentro hacia afuera. Por lo tanto, los argumentos están muy lejos de la
función. Para solucionar este problema, dplyr proporciona el operador
%>%
de magrittr. x %>% f(y)
se convierte
en f(x, y)
para que pueda usarlo para reescribir múltiples
operaciones que puede leer de izquierda a derecha, de arriba a abajo
(leyendo el operador de canalización como “entonces”):
starwars %>%
group_by(species, sex) %>%
select(height, mass) %>%
summarise(
height = mean(height, na.rm = TRUE),
mass = mean(mass, na.rm = TRUE)
)
Los verbos dplyr se pueden clasificar por el tipo de operaciones que realizan. lograr (a veces hablamos de su semántica, es decir, su significado). Es útil tener una buena comprensión de la diferencia entre Operaciones de selección y mutación.
Una de las características atractivas de dplyr es que puede consultar
columnas del tibble como si fueran variables regulares. Sin embargo, la
uniformidad sintáctica de referirse a los nombres de las columnas
desnudas oculta diferencias semánticas entre los verbos. Un símbolo de
columna proporcionado a select()
no tiene el mismo
significado que el mismo símbolo proporcionado para
mutar()
.
Las operaciones de selección esperan nombres de columna y posiciones.
Por lo tanto, cuando llamas a select()
con nombres de
variables desnudos, en realidad representan sus propias posiciones en el
tibble. Las siguientes llamadas son completamente equivalente desde el
punto de vista de dplyr:
# `name` representado por el interger 1
select(starwars, name)
#> # A tibble: 87 × 1
#> name
#> <chr>
#> 1 Luke Skywalker
#> 2 C-3PO
#> 3 R2-D2
#> 4 Darth Vader
#> # … with 83 more rows
select(starwars, 1)
#> # A tibble: 87 × 1
#> name
#> <chr>
#> 1 Luke Skywalker
#> 2 C-3PO
#> 3 R2-D2
#> 4 Darth Vader
#> # … with 83 more rows
Del mismo modo, esto significa que no puede hacer referencia a
variables de el contexto circundante si tienen el mismo nombre que uno
de los columnas En el siguiente ejemplo, height
todavía
representa 2, no 5:
height <- 5
select(starwars, height)
#> # A tibble: 87 × 1
#> height
#> <int>
#> 1 172
#> 2 167
#> 3 96
#> 4 202
#> # … with 83 more rows
Una sutileza útil es que esto sólo se aplica a nombres desnudos y a
seleccionando llamadas como c(altura, masa)
o
altura:masa
. en todos los demás casos, las columnas del
marco de datos no se ponen en el alcance. Esto permite para hacer
referencia a las variables contextuales en los ayudantes de
selección:
name <- "color"
select(starwars, ends_with(name))
#> # A tibble: 87 × 3
#> hair_color skin_color eye_color
#> <chr> <chr> <chr>
#> 1 blond fair blue
#> 2 <NA> gold yellow
#> 3 <NA> white, blue red
#> 4 none white yellow
#> # … with 83 more rows
Estas semánticas suelen ser intuitivas. Pero hay que teber en cuenta la sutil diferencia:
name <- 5
select(starwars, name, identity(name))
#> # A tibble: 87 × 2
#> name skin_color
#> <chr> <chr>
#> 1 Luke Skywalker fair
#> 2 C-3PO gold
#> 3 R2-D2 white, blue
#> 4 Darth Vader white
#> # … with 83 more rows
En el primer argumento, name
representa su propia
posición 1
. En el segundo argumento, name
se
evalúa en el contexto circundante y representa la quinta columna.
Durante mucho tiempo, select()
solía comprender solo las
posiciones de las columnas. Contando desde dplyr 0.6, ahora también
entiende los nombres de las columnas. Este hace que sea un poco más
fácil programar con select()
:
vars <- c("name", "height")
select(starwars, all_of(vars), "mass")
#> # A tibble: 87 × 3
#> name height mass
#> <chr> <int> <dbl>
#> 1 Luke Skywalker 172 77
#> 2 C-3PO 167 75
#> 3 R2-D2 96 32
#> 4 Darth Vader 202 136
#> # … with 83 more rows
La semántica de mutación es bastante diferente de la semántica de
selección. Mientras select()
espera nombres de columna o
posiciones, mutate()
espera vectores de columna.
Configuraremos un tibble más pequeño para usar en nuestros ejemplos.
df <- starwars %>% select(name, height, mass)
Cuando usamos select()
, los nombres de las columnas
desnudas representan su propio posiciones en el tibble. Para
mutate()
por otro lado, la columna los símbolos representan
los vectores de columna reales almacenados en el tibble. Considere lo
que sucede si le damos una cadena o un número a
mutate()
:
mutate(df, "height", 2)
#> # A tibble: 87 × 5
#> name height mass `"height"` `2`
#> <chr> <int> <dbl> <chr> <dbl>
#> 1 Luke Skywalker 172 77 height 2
#> 2 C-3PO 167 75 height 2
#> 3 R2-D2 96 32 height 2
#> 4 Darth Vader 202 136 height 2
#> # … with 83 more rows
mutate()
obtiene vectores de longitud 1 que interpreta
como nuevas columnas en el marco de datos. Estos vectores se reciclan
para que coincidan con el número de filas Es por eso que no tiene
sentido proporcionar expresiones como "altura" + 10
a
mutar()
. ¡Esto equivale a sumar 10 a una cadena! La
expresión correcta es:
mutate(df, height + 10)
#> # A tibble: 87 × 4
#> name height mass `height + 10`
#> <chr> <int> <dbl> <dbl>
#> 1 Luke Skywalker 172 77 182
#> 2 C-3PO 167 75 177
#> 3 R2-D2 96 32 106
#> 4 Darth Vader 202 136 212
#> # … with 83 more rows
De la misma manera, puede quitar las comillas de los valores del contexto si estos los valores representan una columna válida. Deben tener una longitud de 1 (que luego se reciclan) o tienen la misma longitud que el número de filas. En el siguiente ejemplo creamos un nuevo vector que agregamos a los datos marco:
var <- seq(1, nrow(df))
mutate(df, new = var)
#> # A tibble: 87 × 4
#> name height mass new
#> <chr> <int> <dbl> <int>
#> 1 Luke Skywalker 172 77 1
#> 2 C-3PO 167 75 2
#> 3 R2-D2 96 32 3
#> 4 Darth Vader 202 136 4
#> # … with 83 more rows
Un ejemplo de ello es group_by()
. Si bien puede pensar
que ha seleccionado semántica, en realidad tiene una semántica mutada.
Esto es bastante útil ya que permite agrupar por una columna
modificada:
group_by(starwars, sex)
#> # A tibble: 87 × 14
#> # Groups: sex [5]
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 Darth Vader 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
group_by(starwars, sex = as.factor(sex))
#> # A tibble: 87 × 14
#> # Groups: sex [5]
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <fct> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 Darth Vader 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 4 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, and abbreviated variable names
#> # ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year, ⁵homeworld
group_by(starwars, height_binned = cut(height, 3))
#> # A tibble: 87 × 15
#> # Groups: height_binned [4]
#> name height mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex gender homew…⁵
#> <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywal… 172 77 blond fair blue 19 male mascu… Tatooi…
#> 2 C-3PO 167 75 <NA> gold yellow 112 none mascu… Tatooi…
#> 3 R2-D2 96 32 <NA> white,… red 33 none mascu… Naboo
#> 4 Darth Vader 202 136 none white yellow 41.9 male mascu… Tatooi…
#> # … with 83 more rows, 5 more variables: species <chr>, films <list>,
#> # vehicles <list>, starships <list>, height_binned <fct>, and abbreviated
#> # variable names ¹hair_color, ²skin_color, ³eye_color, ⁴birth_year,
#> # ⁵homeworld
Esta es la razón por la que no puede proporcionar un nombre de
columna a group_by()
. Este equivale a crear una nueva
columna que contenga la cadena reciclada al número de filas:
group_by(df, "month")
#> # A tibble: 87 × 4
#> # Groups: "month" [1]
#> name height mass `"month"`
#> <chr> <int> <dbl> <chr>
#> 1 Luke Skywalker 172 77 month
#> 2 C-3PO 167 75 month
#> 3 R2-D2 96 32 month
#> 4 Darth Vader 202 136 month
#> # … with 83 more rows
Corrección de Error y pérdida de información: El mayor desafío dentro de la limpieza de datos es la corrección de valores, pues incluye el quitar duplicados y entradas inválidas. En muchos casos, la información disponible sobre tales anomalías es limitada e insuficiente de determinar las transformaciones necesarias o correcciones abandonando la tachadura de tales entradas como la única solución. La eliminación de datos aunque, conduce a la pérdida de información que puede ser en particular costosa si hay una cantidad grande de datos suprimidos.
Mantenimiento de Datos Limpiados: La limpieza de datos es cara y el tiempo consumido es grande. Después de haber realizado la limpieza de datos y el alcanzar una colección de datos sin errores, uno querría evitar la relimpieza de datos íntegramente después de que se realizan algunos cambios en la base de datos. El proceso sólo debería ser repetido sobre los valores que se han cambiado, esto significa, que debemos guardar un linaje limpiador que requiere una eficiente colección de datos y técnicas de administración de datos.
Limpieza de Datos en Entornos virtualmente Integrados: En Fuentes prácticamente integradas como DiscoveryLink de la IBM, la limpieza de datos tiene que ser realizada siempre con acceso de datos de diferentes fuentes, con una considerable disminución el tiempo de respuesta y la eficacia.
Limpieza de datos en el Framework: En muchos casos no será posible llegar a un completo mapa de limpieza de datos, que guíe el proceso por adelantado. Esto hace que la limpieza de datos sea un proceso iterativo que implica la exploración significativa y la interacción que puede requerir un framework, es decir, un marco que incluya una colección de métodos para la detección de errores y la eliminación además de la revisión de datos. Esto puede ser integrado con otras etapas informáticas como la integración y el mantenimiento.