Chapter 6 Tratamiento de datos en R

El proceso de trabajo de cualquier analista de datos puede dividirse en 4 pasos principales:

  • Recogida de datos.
  • Limpieza o tratamiento de esos de datos.
  • Análisis de los datos.
  • Informe de esos datos.

Generalmente, el segundo punto, el de la limpieza y tratamiento de los datos, suele consumir en torno al 75% del tiempo total. Además, en tiempos donde el Big Data empieza a despuntar, cobra más sentido que nunca dedicar un tiempo importante en preparar todos los datos para analizarlos. Realmente, el Big Data no es otra cosa que una base de datos gigante, que obviamente, no podemos visualizar completamente en un monitor. Cuando trabajamos con estas grandes bases de datos, la limpieza y el orden de la misma es esencial para no llevar a cabo fallos en los análisis.

Dentro de la limpieza de datos, vamos a aprender los siguientes tres puntos:

  • Explorar los datos en bruto.
  • Ordenar los datos.
  • Preparar los datos para el análisis.

6.1 Explorar los datos brutos (raw data)

Cuando tenemos los datos recién recogidos, el siguiente paso es explorar nuestra base de datos sin procesar, o brutos. Este paso requiere a su vez 3 aspectos:

  • Comprender la estructura de tus datos.
  • Observar los datos.
  • Visualizar los datos.

6.1.1 Comprender la estructura de los datos.

La primera cosa que tenemos que hacer cuando recibimos un nuevo conjunto de datos es comprender su estructura. R ofrece algunas funciones para que empecemos a comprender cómo son nuestros datos:

  • class(). Nos indica el tipo de datos que tenemos. Por ejemplo, un data frame.
  • dim(). Nos indica el número de filas y columnas que tienen nuestros datos, y en este orden.
  • names(). Nos indica el nombre de las columnas.
  • str(). Es una de las funciones más versátiles de R ya que, entre otras cosas, agrupa todos los anteriores. Nos devuelve el tipo de datos, las dimensiones, el nombre de columnas y su categoría (si es numérica, carácter, booleana, etc).
  • glimpse(). Esta función está dentro del paquete dplyr y es bastante similar a str().
  • summary(). Nos hace un resumen de la estadística descriptiva de nuestros datos.

6.1.2 Observar los datos

El siguiente paso es ver los datos directamente. Para ello, R nos dota con funciones muy interesantes y útiles para ver los datos, sobre todo cuando su número es muy elevado:

  • head(). Nos muestra las 6 primeras filas. Si queremos otro número, usamos el argumento n.
  • tail(). Nos muestra las 6 últimas filas. Si queremos otro número, usamos el argumento n.
  • print(). Nos visualiza los datos, aunque no está recomendado para conjuntos de datos grandes.

6.1.3 Visualizar los datos

  • hist(). Nos muestra un histograma.
  • plot(). Nos muestra un diagrama de dispersión.

6.2 Ordenando los datos

En 2014, Hadley Wickham publicó un artículo en Journal of Statistical Software titulado Tidy Data, donde se explicaban las nociones de cómo ordenar los datos. Entre estas nociones se encuentran:

  • Las observaciones se representan en filas.
  • Las variables se representan en columnas.
  • Un tipo de unidad observacional por tabla.

6.2.1 Paquete tidyr

El paquete tidyr fue escrito por Hadley Wickham con el propósito de aplicar los principios de la ordenación de datos.

# Cargamos tidyr
library(tidyr)

# Cargamos los datos bmi
bmi <- data.frame(source("Datos/bmi.R"))

6.2.1.1 Función gather() y spread()

Una de las funciones más importantes en tidyr es gather(). Se usa cuando tenemos columnas que no son variales y queremos plegarlas en una variable con esos nombres de las columnas. La función gather() tiene 4 argumentos principales:

  • Nombre del conjunto de datos.
  • Nombre de la nueva columna categórica formada a partir del nombre de las columnas.
  • Nombre de la columna con los valores numéricos.
  • Columna a plegar.

Una consecuencia de la ejecución de esta función es que se generan tablas muy largas.

# Apply gather() to bmi and save the result as bmi_long
bmi_long <- gather(bmi, year, bmi_val, -value.Country)

# View the first 20 rows of the result
head(bmi_long, n = 20)
##          value.Country        year  bmi_val
## 1          Afghanistan value.Y1980 21.48678
## 2              Albania value.Y1980 25.22533
## 3              Algeria value.Y1980 22.25703
## 4              Andorra value.Y1980 25.66652
## 5               Angola value.Y1980 20.94876
## 6  Antigua and Barbuda value.Y1980 23.31424
## 7            Argentina value.Y1980 25.37913
## 8              Armenia value.Y1980 23.82469
## 9            Australia value.Y1980 24.92729
## 10             Austria value.Y1980 24.84097
## 11          Azerbaijan value.Y1980 24.49375
## 12             Bahamas value.Y1980 24.21064
## 13             Bahrain value.Y1980 23.97588
## 14          Bangladesh value.Y1980 20.51918
## 15            Barbados value.Y1980 24.36372
## 16             Belarus value.Y1980 24.90898
## 17             Belgium value.Y1980 25.09879
## 18              Belize value.Y1980 24.54345
## 19               Benin value.Y1980 20.80754
## 20             Bermuda value.Y1980 25.07881

La funcion spread() hace justo lo contrario a gather().

bmi_wide <- spread(bmi_long, year, bmi_val)
head(bmi_wide, n = 20)
##          value.Country value.Y1980 value.Y1981 value.Y1982 value.Y1983
## 1          Afghanistan    21.48678    21.46552    21.45145    21.43822
## 2              Albania    25.22533    25.23981    25.25636    25.27176
## 3              Algeria    22.25703    22.34745    22.43647    22.52105
## 4              Andorra    25.66652    25.70868    25.74681    25.78250
## 5               Angola    20.94876    20.94371    20.93754    20.93187
## 6  Antigua and Barbuda    23.31424    23.39054    23.45883    23.53735
## 7            Argentina    25.37913    25.44951    25.50242    25.55644
## 8              Armenia    23.82469    23.86401    23.91023    23.95649
## 9            Australia    24.92729    25.00216    25.07660    25.14938
## 10             Austria    24.84097    24.88110    24.93482    24.98118
## 11          Azerbaijan    24.49375    24.52584    24.56064    24.60150
## 12             Bahamas    24.21064    24.30814    24.42750    24.54415
## 13             Bahrain    23.97588    24.09045    24.20617    24.32335
## 14          Bangladesh    20.51918    20.47766    20.43741    20.40075
## 15            Barbados    24.36372    24.43455    24.49314    24.54713
## 16             Belarus    24.90898    24.90986    24.91623    24.91568
## 17             Belgium    25.09879    25.12474    25.16059    25.18982
## 18              Belize    24.54345    24.63418    24.71853    24.76496
## 19               Benin    20.80754    20.83999    20.88208    20.90821
## 20             Bermuda    25.07881    25.19625    25.31163    25.43990
##    value.Y1984 value.Y1985 value.Y1986 value.Y1987 value.Y1988 value.Y1989
## 1     21.42734    21.41222    21.40132    21.37679    21.34018    21.29845
## 2     25.27901    25.28669    25.29451    25.30217    25.30450    25.31944
## 3     22.60633    22.69501    22.76979    22.84096    22.90644    22.97931
## 4     25.81874    25.85236    25.89089    25.93414    25.98477    26.04450
## 5     20.93569    20.94857    20.96030    20.98025    21.01375    21.05269
## 6     23.63584    23.73109    23.83449    23.93649    24.05364    24.16347
## 7     25.61271    25.66593    25.72364    25.78529    25.84428    25.88510
## 8     24.00181    24.04083    24.08736    24.13334    24.17219    24.19556
## 9     25.22894    25.31849    25.41017    25.50528    25.60001    25.70050
## 10    25.02208    25.06015    25.10680    25.14747    25.19333    25.24928
## 11    24.64121    24.67566    24.71906    24.75799    24.78894    24.82277
## 12    24.66558    24.78408    24.90724    25.03166    25.14778    25.26173
## 13    24.43174    24.53684    24.63328    24.74914    24.86604    24.98644
## 14    20.36524    20.32983    20.29654    20.26401    20.23497    20.20736
## 15    24.59913    24.64998    24.71728    24.77976    24.84265    24.90790
## 16    24.91640    24.92013    24.93057    24.94083    24.96638    24.99085
## 17    25.21949    25.25266    25.28532    25.32356    25.37574    25.42939
## 18    24.81656    24.85631    24.90216    24.95997    25.01621    25.08335
## 19    20.96054    21.00726    21.05137    21.07871    21.12584    21.16916
## 20    25.56404    25.69903    25.82431    25.94406    26.06962    26.19307
##    value.Y1990 value.Y1991 value.Y1992 value.Y1993 value.Y1994 value.Y1995
## 1     21.24818    21.20269    21.14238    21.06376    20.97987    20.91132
## 2     25.32357    25.28452    25.23077    25.21192    25.22115    25.25874
## 3     23.04600    23.11333    23.18776    23.25764    23.32273    23.39526
## 4     26.10936    26.17912    26.24017    26.30356    26.36793    26.43569
## 5     21.09007    21.12136    21.14987    21.13938    21.14186    21.16022
## 6     24.26782    24.36568    24.45644    24.54096    24.60945    24.66461
## 7     25.92482    25.99177    26.07642    26.17288    26.27872    26.37522
## 8     24.20618    24.19790    24.12982    24.05854    24.02297    24.01570
## 9     25.80568    25.90295    26.00624    26.10586    26.20077    26.29241
## 10    25.30882    25.37186    25.43668    25.50507    25.56626    25.61814
## 11    24.83167    24.83972    24.81781    24.76250    24.69113    24.61946
## 12    25.35641    25.44039    25.51294    25.58479    25.65514    25.72418
## 13    25.11479    25.25103    25.40173    25.56146    25.71611    25.87566
## 14    20.18246    20.15921    20.14118    20.12952    20.11823    20.10770
## 15    24.96113    25.00859    25.05249    25.09414    25.14401    25.20411
## 16    25.01652    25.05106    25.07413    25.09742    25.10406    25.10237
## 17    25.49029    25.55918    25.64048    25.70620    25.77927    25.84461
## 18    25.16254    25.23639    25.34573    25.45261    25.54387    25.64134
## 19    21.20891    21.25774    21.30311    21.35252    21.40226    21.45576
## 20    26.30429    26.40672    26.53011    26.65258    26.75763    26.88993
##    value.Y1996 value.Y1997 value.Y1998 value.Y1999 value.Y2000 value.Y2001
## 1     20.85155    20.81307    20.78591    20.75469    20.69521    20.62643
## 2     25.31097    25.33988    25.39116    25.46555    25.55835    25.66701
## 3     23.46811    23.54160    23.61592    23.69486    23.77659    23.86256
## 4     26.50769    26.58255    26.66337    26.75078    26.83179    26.92373
## 5     21.19076    21.22621    21.27082    21.31954    21.37480    21.43664
## 6     24.72544    24.78714    24.84936    24.91721    24.99158    25.05857
## 7     26.47182    26.57778    26.68714    26.79005    26.88103    26.96067
## 8     24.02627    24.03885    24.07100    24.11699    24.18045    24.26670
## 9     26.38256    26.47351    26.56314    26.65506    26.74486    26.84397
## 10    25.66410    25.71737    25.75996    25.81773    25.87471    25.93806
## 11    24.55527    24.49745    24.47179    24.47842    24.51287    24.57202
## 12    25.79938    25.89374    25.99417    26.12080    26.25748    26.38653
## 13    26.03138    26.18600    26.34294    26.50245    26.65409    26.80388
## 14    20.10489    20.11304    20.12622    20.13361    20.14774    20.16802
## 15    25.26850    25.35236    25.42067    25.51681    25.60292    25.68910
## 16    25.11362    25.14026    25.17221    25.20688    25.25472    25.32148
## 17    25.91482    25.98577    26.05275    26.12240    26.18388    26.24196
## 18    25.72576    25.80637    25.88939    25.97705    26.08213    26.19233
## 19    21.52050    21.58523    21.64874    21.71747    21.79710    21.87272
## 20    27.01108    27.12503    27.24352    27.34456    27.45476    27.55826
##    value.Y2002 value.Y2003 value.Y2004 value.Y2005 value.Y2006 value.Y2007
## 1     20.59848    20.58706    20.57759    20.58084    20.58749    20.60246
## 2     25.77167    25.87274    25.98136    26.08939    26.20867    26.32753
## 3     23.95294    24.05243    24.15957    24.27001    24.38270    24.48846
## 4     27.02525    27.12481    27.23107    27.32827    27.43588    27.53363
## 5     21.51765    21.59924    21.69218    21.80564    21.93881    22.08962
## 6     25.13039    25.20713    25.29898    25.39965    25.51382    25.64247
## 7     26.99882    27.04738    27.11001    27.18941    27.28179    27.38889
## 8     24.37698    24.50332    24.64178    24.81447    24.99160    25.17590
## 9     26.93858    27.03801    27.13871    27.24614    27.35267    27.45878
## 10    25.99583    26.06356    26.14360    26.21107    26.29374    26.38136
## 11    24.66021    24.77164    24.89376    25.06256    25.25706    25.45513
## 12    26.51184    26.62607    26.75612    26.88517    27.00715    27.12653
## 13    26.94923    27.09298    27.23908    27.38693    27.53868    27.68865
## 14    20.18621    20.20948    20.23957    20.27648    20.31554    20.35493
## 15    25.77615    25.87020    25.95660    26.06074    26.16874    26.27575
## 16    25.40160    25.48133    25.58449    25.70824    25.85820    26.01110
## 17    26.30602    26.37453    26.44661    26.51575    26.59522    26.67529
## 18    26.30847    26.43920    26.56389    26.68025    26.80065    26.90962
## 19    21.95309    22.02760    22.10390    22.17929    22.25322    22.33366
## 20    27.66492    27.76999    27.87082    27.98684    28.12057    28.26753
##    value.Y2008 visible
## 1     20.62058       1
## 2     26.44657       1
## 3     24.59620       1
## 4     27.63048       1
## 5     22.25083       1
## 6     25.76602       1
## 7     27.50170       1
## 8     25.35542       1
## 9     27.56373       1
## 10    26.46741       1
## 11    25.65117       1
## 12    27.24594       1
## 13    27.83721       1
## 14    20.39742       1
## 15    26.38439       1
## 16    26.16443       1
## 17    26.75915       1
## 18    27.02255       1
## 19    22.41835       1
## 20    28.41894       1

6.2.1.2 Función separate() y unite()

# Cargamos los datos:
treatments <- structure(list(patient = structure(c(1L, 2L, 1L, 2L, 1L, 2L), .Label = c("X", 
"Y"), class = "factor"), treatment = c("A", "A", "B", "B", "C", 
"C"), year_mo = c("2010-10", "2010-10", "2012-08", "2012-08", 
"2014-12", "2014-12"), response = c(1, 4, 2, 5, 3, 6)), class = "data.frame", row.names = c(NA, 
-6L), .Names = c("patient", "treatment", "year_mo", "response"
))

La función separate lo que hace es separar el contenido de una columna en varias columnas utilizando caracteres que estén en su contenido. Tiene tres argumentos obligatorios y uno opcional:

  • data. El nombre del data frame.
  • column. La columna que queremos separar en varias.
  • into. Vector de caracteres con el nombre de las columnas nuevas. De modo predeterminado usa el espacio, punto, barra o guión ( ./-) como separadores.
  • sep. El símbolo que vamos a usar para separar el contenido. Lo usamos cuando el símbolo no es uno de los anteriores sino que le especificamos cuál queremos exactamente.
# Separamos la columna year_mo en year y month.
treat_sep <- separate(treatments, year_mo, c("year", "month"))
print(treat_sep)
##   patient treatment year month response
## 1       X         A 2010    10        1
## 2       Y         A 2010    10        4
## 3       X         B 2012    08        2
## 4       Y         B 2012    08        5
## 5       X         C 2014    12        3
## 6       Y         C 2014    12        6

La función unite() hace justo lo contrario que separate(), une varias columnas en una sola.

  • data. El nombre del data frame.
  • col. Especificamos el nombre de la columna nueva.
  • .... Indicamos el nombre de las columnas a unir.
  • sep. Indicamos el símbolo que va a usar para unir el contenido de las columnas. De modo predeterminado usa el guión bajo (_).
unite(treat_sep, YEAR_MONTH, year, month, sep = "-")
##   patient treatment YEAR_MONTH response
## 1       X         A    2010-10        1
## 2       Y         A    2010-10        4
## 3       X         B    2012-08        2
## 4       Y         B    2012-08        5
## 5       X         C    2014-12        3
## 6       Y         C    2014-12        6

6.2.2 Ejemplos de datos desordenados

  • El nombre de las columnas corresponde a valores, y no a nombres de variables. Esto sucede cuando por ejemplo ponemos variables de presencia/ausencia, en color de ojos. En vez de poner una columna con el color de ojos, se ponen 3 columnas con ojos_azules, ojos_marrones y ojos_verdes, por ejemplo.

  • Tener una tabla el contenido de dos. Veamos el siguiente ejemplo, donde se contabilizan el número de mascotas por persona. Vemos que la primera columna, los valores se repiten porque cada persona tiene varios tipos de mascotas. Esto es claramente un ejemplo de datos desordenados, ya que la información está duplicada.

# Cargamos unos datos de ejemplo

pets <- structure(list(owner = c("Jason", "Jason", "Jason", "Lisa", "Lisa", 
"Lisa", "Terrence", "Terrence", "Terrence"), type = c("dog", 
"cat", "bird", "dog", "cat", "bird", "dog", "cat", "bird"), num = c(2L, 
4L, 3L, 7L, 10L, 9L, 8L, 5L, 1L)), .Names = c("owner", "type", 
"num"), class = "data.frame", row.names = c(NA, -9L))

print(pets)
##      owner type num
## 1    Jason  dog   2
## 2    Jason  cat   4
## 3    Jason bird   3
## 4     Lisa  dog   7
## 5     Lisa  cat  10
## 6     Lisa bird   9
## 7 Terrence  dog   8
## 8 Terrence  cat   5
## 9 Terrence bird   1

6.3 Preparar los datos para análisis

6.3.1 Conversiones entre tipos de variables

Para poder hacer conversiones entre variables de diferentes tipos en R, primero debemos conocer qué tipos de variables existen:

  • character: “tratamiento”, “435”, “B”. Los elementos van entre comillas.
  • numeric: 45.34, 342, NaN, Inf. Son números, con o sin decimales, así como otros especiales como NaN (Not a Number) o Inf (Infinite).
  • integer: 1L, 1234L. Son números enteros, sin decimales. Van acompañados de una L al final.
  • factor: factor(“Hola”). Es interesante cuando tenemos variables categóricas.
  • logical: TRUE, FALSE, NA.

Podemos ver qué tipo de variable tenemos con la función class().

# Make this evaluate to "character"
class("TRUE")
## [1] "character"
# Make this evaluate to "numeric"
class(8484.00)
## [1] "numeric"
# Make this evaluate to "integer"
class(99L)
## [1] "integer"
# Make this evaluate to "factor"
class(factor("factor"))
## [1] "factor"
# Make this evaluate to "logical"
class(FALSE)
## [1] "logical"

Asimismo, podemos forzar a R a que considere un cierto objeto como un tipo concreto con las siguientes funciones:

as.character(2018) # 2018 se convierte en character.
## [1] "2018"
as.numeric(FALSE) # convierte FALSE en número
## [1] 0
as.integer(50) # convierte el numérico 50 en entero, aunque no imprima la L
## [1] 50
as.factor("hola") # convierte hola en variable factorial o categórica
## [1] hola
## Levels: hola
as.logical(1) # convierte el 1 en TRUE
## [1] TRUE
# We've loaded a dataset called students into your workspace. These data provide information on 395 students including their grades in three classes (in the Grades column, separated by /).
students <- as.data.frame(source("Datos/students.R"))

# Preview students with str()
str(students)
## 'data.frame':    395 obs. of  32 variables:
##  $ value.school    : Factor w/ 2 levels "GP","MS": 1 1 1 1 1 1 1 1 1 1 ...
##  $ value.sex       : Factor w/ 2 levels "F","M": 1 1 1 1 1 2 2 1 2 2 ...
##  $ value.age       : int  18 17 15 15 16 16 16 17 15 15 ...
##  $ value.address   : Factor w/ 2 levels "R","U": 2 2 2 2 2 2 2 2 2 2 ...
##  $ value.famsize   : Factor w/ 2 levels "GT3","LE3": 1 1 2 1 1 2 2 1 2 1 ...
##  $ value.Pstatus   : Factor w/ 2 levels "A","T": 1 2 2 2 2 2 2 1 1 2 ...
##  $ value.Medu      : int  4 1 1 4 3 4 2 4 3 3 ...
##  $ value.Fedu      : int  4 1 1 2 3 3 2 4 2 4 ...
##  $ value.Mjob      : Factor w/ 5 levels "at_home","health",..: 1 1 1 2 3 4 3 3 4 3 ...
##  $ value.Fjob      : Factor w/ 5 levels "at_home","health",..: 5 3 3 4 3 3 3 5 3 3 ...
##  $ value.reason    : Factor w/ 4 levels "course","home",..: 1 1 3 2 2 4 2 2 2 2 ...
##  $ value.guardian  : Factor w/ 3 levels "father","mother",..: 2 1 2 2 1 2 2 2 2 2 ...
##  $ value.traveltime: int  2 1 1 1 1 1 1 2 1 1 ...
##  $ value.studytime : int  2 2 2 3 2 2 2 2 2 2 ...
##  $ value.failures  : int  0 0 3 0 0 0 0 0 0 0 ...
##  $ value.schoolsup : Factor w/ 2 levels "no","yes": 2 1 2 1 1 1 1 2 1 1 ...
##  $ value.famsup    : Factor w/ 2 levels "no","yes": 1 2 1 2 2 2 1 2 2 2 ...
##  $ value.paid      : Factor w/ 2 levels "no","yes": 1 1 2 2 2 2 1 1 2 2 ...
##  $ value.activities: Factor w/ 2 levels "no","yes": 1 1 1 2 1 2 1 1 1 2 ...
##  $ value.nursery   : Factor w/ 2 levels "no","yes": 2 1 2 2 2 2 2 2 2 2 ...
##  $ value.higher    : Factor w/ 2 levels "no","yes": 2 2 2 2 2 2 2 2 2 2 ...
##  $ value.internet  : Factor w/ 2 levels "no","yes": 1 2 2 2 1 2 2 1 2 2 ...
##  $ value.romantic  : Factor w/ 2 levels "no","yes": 1 1 1 2 1 1 1 1 1 1 ...
##  $ value.famrel    : int  4 5 4 3 4 5 4 4 4 5 ...
##  $ value.freetime  : int  3 3 3 2 3 4 4 1 2 5 ...
##  $ value.goout     : int  4 3 2 2 2 2 4 4 2 1 ...
##  $ value.Dalc      : int  1 1 2 1 1 1 1 1 1 1 ...
##  $ value.Walc      : int  1 1 3 1 2 2 1 1 1 1 ...
##  $ value.health    : int  3 3 3 5 5 5 3 1 1 5 ...
##  $ value.absences  : int  6 4 10 2 4 10 0 6 0 0 ...
##  $ value.Grades    : Factor w/ 197 levels "10/0/0","10/10/0",..: 124 123 154 86 128 88 46 131 104 77 ...
##  $ visible         : logi  TRUE TRUE TRUE TRUE TRUE TRUE ...
# Coerce Grades to character
students$Grades <- as.character(students$value.Grades)

# Coerce Medu to factor
students$Medu <- as.factor(factor(students$value.Medu))

# Coerce Fedu to factor
students$Fedu <- as.factor(factor(students$value.Fedu))
    
# Look at students once more with str()
str(students)
## 'data.frame':    395 obs. of  35 variables:
##  $ value.school    : Factor w/ 2 levels "GP","MS": 1 1 1 1 1 1 1 1 1 1 ...
##  $ value.sex       : Factor w/ 2 levels "F","M": 1 1 1 1 1 2 2 1 2 2 ...
##  $ value.age       : int  18 17 15 15 16 16 16 17 15 15 ...
##  $ value.address   : Factor w/ 2 levels "R","U": 2 2 2 2 2 2 2 2 2 2 ...
##  $ value.famsize   : Factor w/ 2 levels "GT3","LE3": 1 1 2 1 1 2 2 1 2 1 ...
##  $ value.Pstatus   : Factor w/ 2 levels "A","T": 1 2 2 2 2 2 2 1 1 2 ...
##  $ value.Medu      : int  4 1 1 4 3 4 2 4 3 3 ...
##  $ value.Fedu      : int  4 1 1 2 3 3 2 4 2 4 ...
##  $ value.Mjob      : Factor w/ 5 levels "at_home","health",..: 1 1 1 2 3 4 3 3 4 3 ...
##  $ value.Fjob      : Factor w/ 5 levels "at_home","health",..: 5 3 3 4 3 3 3 5 3 3 ...
##  $ value.reason    : Factor w/ 4 levels "course","home",..: 1 1 3 2 2 4 2 2 2 2 ...
##  $ value.guardian  : Factor w/ 3 levels "father","mother",..: 2 1 2 2 1 2 2 2 2 2 ...
##  $ value.traveltime: int  2 1 1 1 1 1 1 2 1 1 ...
##  $ value.studytime : int  2 2 2 3 2 2 2 2 2 2 ...
##  $ value.failures  : int  0 0 3 0 0 0 0 0 0 0 ...
##  $ value.schoolsup : Factor w/ 2 levels "no","yes": 2 1 2 1 1 1 1 2 1 1 ...
##  $ value.famsup    : Factor w/ 2 levels "no","yes": 1 2 1 2 2 2 1 2 2 2 ...
##  $ value.paid      : Factor w/ 2 levels "no","yes": 1 1 2 2 2 2 1 1 2 2 ...
##  $ value.activities: Factor w/ 2 levels "no","yes": 1 1 1 2 1 2 1 1 1 2 ...
##  $ value.nursery   : Factor w/ 2 levels "no","yes": 2 1 2 2 2 2 2 2 2 2 ...
##  $ value.higher    : Factor w/ 2 levels "no","yes": 2 2 2 2 2 2 2 2 2 2 ...
##  $ value.internet  : Factor w/ 2 levels "no","yes": 1 2 2 2 1 2 2 1 2 2 ...
##  $ value.romantic  : Factor w/ 2 levels "no","yes": 1 1 1 2 1 1 1 1 1 1 ...
##  $ value.famrel    : int  4 5 4 3 4 5 4 4 4 5 ...
##  $ value.freetime  : int  3 3 3 2 3 4 4 1 2 5 ...
##  $ value.goout     : int  4 3 2 2 2 2 4 4 2 1 ...
##  $ value.Dalc      : int  1 1 2 1 1 1 1 1 1 1 ...
##  $ value.Walc      : int  1 1 3 1 2 2 1 1 1 1 ...
##  $ value.health    : int  3 3 3 5 5 5 3 1 1 5 ...
##  $ value.absences  : int  6 4 10 2 4 10 0 6 0 0 ...
##  $ value.Grades    : Factor w/ 197 levels "10/0/0","10/10/0",..: 124 123 154 86 128 88 46 131 104 77 ...
##  $ visible         : logi  TRUE TRUE TRUE TRUE TRUE TRUE ...
##  $ Grades          : chr  "5/6/6" "5/5/6" "7/8/10" "15/14/15" ...
##  $ Medu            : Factor w/ 5 levels "0","1","2","3",..: 5 2 2 5 4 5 3 5 4 4 ...
##  $ Fedu            : Factor w/ 5 levels "0","1","2","3",..: 5 2 2 3 4 4 3 5 3 5 ...

6.3.1.1 Paquete lubridate

Si queremos convertir objetos que representan tiempo (fechas y horas) en realmente los formatos adecuados de R para interpretar esas fechas y horas, podemos usar el paquete lubridate, escrito por Garrett Grolemund y Hadley Wickham.

library(lubridate) # cargamos el paquete
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:data.table':
## 
##     hour, isoweek, mday, minute, month, quarter, second, wday,
##     week, yday, year
## The following object is masked from 'package:base':
## 
##     date
ymd("2018-9-8") # convertimos year-mont-day
## [1] "2018-09-08"
ymd("2018 September 8th") # igual que el anterior pero con otro formato
## [1] "2018-09-08"
mdy("9/8/2018") # convertimos month-day-year
## [1] "2018-09-08"
dmy("8-09/2018")
## [1] "2018-09-08"
hms("6:30:00") # convertimos hour-minut-second
## [1] "6H 30M 0S"
ymd_hms ("2018,09,8 06-30-00") # combinamos las fechas y horas
## [1] "2018-09-08 06:30:00 UTC"
mdy_hm("September 8, 2018 06:30") # no ponemos segundos
## [1] "2018-09-08 06:30:00 UTC"

Como hemos podido ver, las funciones anteriores hacen lo mejor que pueden para interpretar correctamente las fechas y horas, independientemente de los símbolos que separan los elementos, etc. Y ciertamente, el orden de y, m, d, h, m y s en las funciones debe casar con el orden de la fecha y hora que queramos importar correctamente a R.

students2 <- as.data.frame(source("Datos/students2.R"))

# Convertimos columna value.dob en fecha
students2$value.dob <- ymd(students2$value.dob)
students2$value.nurse_visit <- ymd_hms(students2$value.nurse_visit)

head(students2)
##   value.X value.school value.sex  value.dob value.address value.famsize
## 1       1           GP         F 2000-06-05             U           GT3
## 2       2           GP         F 1999-11-25             U           GT3
## 3       3           GP         F 1998-02-02             U           LE3
## 4       4           GP         F 1997-12-20             U           GT3
## 5       5           GP         F 1998-10-04             U           GT3
## 6       6           GP         M 1999-06-16             U           LE3
##   value.Pstatus value.Medu value.Fedu value.Mjob value.Fjob value.reason
## 1             A          4          4    at_home    teacher       course
## 2             T          1          1    at_home      other       course
## 3             T          1          1    at_home      other        other
## 4             T          4          2     health   services         home
## 5             T          3          3      other      other         home
## 6             T          4          3   services      other   reputation
##   value.guardian value.traveltime value.studytime value.failures
## 1         mother                2               2              0
## 2         father                1               2              0
## 3         mother                1               2              3
## 4         mother                1               3              0
## 5         father                1               2              0
## 6         mother                1               2              0
##   value.schoolsup value.famsup value.paid value.activities value.nursery
## 1             yes           no         no               no           yes
## 2              no          yes         no               no            no
## 3             yes           no        yes               no           yes
## 4              no          yes        yes              yes           yes
## 5              no          yes        yes               no           yes
## 6              no          yes        yes              yes           yes
##   value.higher value.internet value.romantic value.famrel value.freetime
## 1          yes             no             no            4              3
## 2          yes            yes             no            5              3
## 3          yes            yes             no            4              3
## 4          yes            yes            yes            3              2
## 5          yes             no             no            4              3
## 6          yes            yes             no            5              4
##   value.goout value.Dalc value.Walc value.health   value.nurse_visit
## 1           4          1          1            3 2014-04-10 14:59:54
## 2           3          1          1            3 2015-03-12 14:59:54
## 3           2          2          3            3 2015-09-21 14:59:54
## 4           2          1          1            5 2015-09-03 14:59:54
## 5           2          1          2            5 2015-04-07 14:59:54
## 6           2          1          2            5 2013-11-15 14:59:54
##   value.absences value.Grades visible
## 1              6        5/6/6    TRUE
## 2              4        5/5/6    TRUE
## 3             10       7/8/10    TRUE
## 4              2     15/14/15    TRUE
## 5              4      6/10/10    TRUE
## 6             10     15/15/15    TRUE

6.3.2 Manipulación de cadenas de caracteres

Para manipular cadenas de caracteres, Hadley Wickham desarrolló un paquete fantástico: stringr. Vamos a ver las funciones principales.

# Cargamos stringr
library(stringr)

6.3.2.1 str_trim(): eliminar espacios en blanco innecesarios

Un paso importante a la hora de limpiar los datos, y en este caso los caracteres numéricos, es eliminar los espacios en blanco innecesarios al principio y al final de las cadenas de caracteres. Esto lo podemos solventar con la función str_trim().

str_trim("         Esto es una prueba de cómo funciona     ")
## [1] "Esto es una prueba de cómo funciona"
str_trim(c("   Mario  ", "Marina    ", "        Luis"))
## [1] "Mario"  "Marina" "Luis"

6.3.2.2 str_pad(): rellenar cadenas de caracteres

Pongamos el caso de que la identificación de productos en una empresa está codificado por 10 números, pero no todos ellos tienen todos esos caracteres. Algunos tienen 7, otros 8, otros 3 etc. Sin embargo, hay que rellenar con ceros a la izquierda tantas veces como espacios falten hasta hacer 10. Esto lo podemos solucionar con str_pad().

str_pad("54862", # este sería el número del producto 
        width = 10, # este es el número de caracteres final
        side = "left", # posición de donde se añadirán caracteres
        pad = "0" # que se rellene hasta 10 con ceros
        ) 
## [1] "0000054862"

6.3.2.3 toupper() y tolower(): convertir en mayúsculas y minúsculas

Las funciones toupper() y tolower() nos permiten convertir una cadena de caracteres en todo mayúsculas y todo minúsculas respectivamente.

paises <- c("es", "en", "br", "fr", "pt")
toupper(paises)
## [1] "ES" "EN" "BR" "FR" "PT"
tolower ("ESTOY APRENDIENDO A CONVERTIR EN MINÚSCULAS")
## [1] "estoy aprendiendo a convertir en minúsculas"

6.3.2.4 str_detect(): detectar caracteres en cadenas

Si queremos localizar la presencia de un carácter o una cadena de caracteres, usamos str_detect(). Nos devuelve un resultado lógico TRUE o FALSE. Primero ponemos las cadenas donde buscar, y el segundo argumento lo forma lo que queremos buscar.

# Vamos a ver si aparece la palabra Mario
str_detect(c("Mario", "Rodolfo", "Valentina", "Victoria"), "Mario")
## [1]  TRUE FALSE FALSE FALSE
# Vamos a ver la presencia de la letra "o"
str_detect(c("Mario", "Rodolfo", "Valentina", "Victoria"), "o")
## [1]  TRUE  TRUE FALSE  TRUE

6.3.2.5 str_replace() y str_replace_all(): reemplazar caracteres

Si queremos cambiar un carácter o cadena por otro carácter u otra cadena, usamos la función str_replace() (te cambia la primera vez que encuentra la condición) y la función str_replace_all() (te cambia todo).

# Vamos a cambiar la letra "o" por una "a"
str_replace(c("Mario", "Rodolfo", "Valentina", "Victoria"), "o", "a")
## [1] "Maria"     "Radolfo"   "Valentina" "Victaria"
str_replace_all(c("Mario", "Rodolfo", "Valentina", "Victoria"), "o", "a")
## [1] "Maria"     "Radalfa"   "Valentina" "Victaria"

Vemos que la palabra Rodolfo, al cambiar la “o” por la “a” usando str_replace() solo cambia la primera “o”. Sin embargo, al usar str_replace_all(), te lo cambia completamente.

6.3.3 Valores especiales y ausentes

Debemos ofrecer un tratamiento especial cuando trabajemos con valores ausentes (NA, que significa Not Available) o con valores especiales (Inf, de Infinite, por ejemplo).

6.3.3.1 Valores ausentes: NA

Cuando tenemos una base de datos, suele ser corriente la presencia de celdas vacías, que no contienen información. Estas celdas en R suelen representarse como NA.

# Creamos un conjunto de datos con NA en su interior
df <- data.frame(A = c(1, NA, 5, NA),
                 B = c(4, NA, 74, 43),
                 C = c(4, 76, 32, 2))

# Revisamos los NA que hay
is.na(df)
##          A     B     C
## [1,] FALSE FALSE FALSE
## [2,]  TRUE  TRUE FALSE
## [3,] FALSE FALSE FALSE
## [4,]  TRUE FALSE FALSE
# ¿Existe algún NA en el conjunto de datos?
any(is.na(df))
## [1] TRUE
# ¿Cuántos NA hay?
sum(is.na(df))
## [1] 3
# Con summary también vemos el número de NA
summary(df)
##        A           B               C       
##  Min.   :1   Min.   : 4.00   Min.   : 2.0  
##  1st Qu.:2   1st Qu.:23.50   1st Qu.: 3.5  
##  Median :3   Median :43.00   Median :18.0  
##  Mean   :3   Mean   :40.33   Mean   :28.5  
##  3rd Qu.:4   3rd Qu.:58.50   3rd Qu.:43.0  
##  Max.   :5   Max.   :74.00   Max.   :76.0  
##  NA's   :2   NA's   :1
# Usando table en columna A
table(df$A)
## 
## 1 5 
## 1 1

Si tomamos la decisión de eliminar las observaciones con NA, podemos usar complete.cases().

complete.cases(df)
## [1]  TRUE FALSE  TRUE FALSE
social_df <- structure(list(name = structure(c(3L, 4L, 2L, 1L), .Label = c("Alice", 
"David", "Sarah", "Tom"), class = "factor"), n_friends = c(244, 
NA, 145, 43), status = structure(c(2L, NA, 3L, NA), .Label = c("", 
"Going out!", "Movie night..."), class = "factor")), .Names = c("name", 
"n_friends", "status"), row.names = c(NA, -4L), class = "data.frame")

# Replace all empty strings in status with NA
social_df$status[social_df$status == ""] <- NA

# Print social_df to the console
print(social_df)
##    name n_friends         status
## 1 Sarah       244     Going out!
## 2   Tom        NA           <NA>
## 3 David       145 Movie night...
## 4 Alice        43           <NA>
# Use complete.cases() to see which rows have no missing values
complete.cases(social_df)
## [1]  TRUE FALSE  TRUE FALSE
# Use na.omit() to remove all rows with any missing values
na.omit(social_df)
##    name n_friends         status
## 1 Sarah       244     Going out!
## 3 David       145 Movie night...

6.3.4 Datos anómalos y errores

Cuando en nuestros datos, observamos valores extraños, debemos decidir si realmente son valores reales, aunque extraños, o son realmente errores. Por ejemplo, si estamos trabajando con edades de personas, valores negativos o superiores a 150 años, por ejemplo, serían considerados sin duda como errores. Estos errores pueden tener un origen diverso, como errores en la introducción de los datos, en la toma de la medida, etc. 3 modos principales nos valen para poder identificar datos anómalos (o outliers):

  • summary(). Un resumen de los datos.
  • hist(). Representamos un histograma.
  • boxplot(). Representamos un diagrama de caja y bigotes.