Definición y características.

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:

  • Exactitud: Los datos deben cumplir los requisitos de integridad, consistencia y densidad.
  • Integridad: Los datos deben cumplir los requisitos de Entereza y validez.
  • Entereza: Alcanzado por la corrección de datos que contienen anomalías.
  • Validez: Alcanzado por la cantidad de datos que satisfacen las restricciones de integridad.
  • Consistencia: Alcanzado por la corrección de contradicciones y anomalías sintácticas.
  • Uniformidad: Relacionado con irregularidades.
  • Densidad: Conocer el cociente de valores omitidos sobre el número de valores totales.
  • Unicidad: Relacionado con datos duplicados.

Importancia de la limpieza de datos

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)

Procesos de limpieza de datos

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:

  • Método Estadístico: Incluye analizar los datos usando promedios, desviación estándar, rangos, o algoritmos de cluster, este análisis se realiza por expertos que identifican errores. Aunque la corrección de datos sea difícil ya que no saben el valor verdadero, pueden ser resueltos poniendo los valores a un promedio u otro valor estadístico. Los métodos estadísticos también pueden ser usados para manejar los valores que fallan, que pueden ser substituidos por uno o varios valores posibles que por lo general son obtenidos por algoritmos de aumento de datos extensos.

Herramientas de Limpieza de Datos

Hay muchas herramientas de limpieza de datos como:

  • Trifacta

  • Openrefine

  • Praxata

  • alterix

  • 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

Limpieza de datos con 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.

Instalación

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”)

Primeros pasos dplyr

Al trabajar con datos debe:

  • Averigua lo que quieres hacer.
  • Describe esas tareas en forma de un programa de computadora.
  • Ejecutar el programa.

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.

Data: starwars

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().

Verbos de una sola tabla

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.

PIPA

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.

Filtra filas con 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", ]

Organiza filas con 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

Elige filas usando su posición con 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

Seleccionar con 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

Agregar nuevas columnas con 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

Cambiar el orden de las columnas con 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

Resumir valores con 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.

Puntos en comú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()).

Combinando funciones con %>%

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)
  )

Patrones de operaciones

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.

Selección de operaciones

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

Operaciones de mutación

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

Desafíos y Problemas

  • 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.

Referencias

  • Nigrini, M. Forensic Analytics: Methods and Techniques for Forensic Accounting Investigations, Wiley. 2011
  • The importance of data cleansing user-generated-content Publicado por spotlessdata.com
  • Trifacta
  • OpenRefine
  • Paxata
  • Alteryx
  • Han, J., Kamber, M. Data Mining: Concepts and Techniques, Morgan Kaufmann, 2001. ISBN 1-55860-489-8.
  • Kimball, R., Caserta, J. The Data Warehouse ETL Toolkit, Wiley and Sons, 2004. ISBN 0-7645-6757-8.
  • Muller H., Freytag J., Problems, Methods, and Challenges in Comprehensive Data Cleansing, Humboldt-Universitat zu Berlin, Germany.
  • Rahm, E., Hong, H. Data Cleaning: Problems and Current Approaches, University of Leipzig, Germany.