Limpieza y análisis de bases de datos

Thu, Jul 23, 2020 28-minute read

La mayor parte de los resultados de las encuestas son presentados en partes, y el vaciado de las respuestas no siempre nos permite hacer un análisis preciso de la información. Por esta razón, resulta indispensable hacer un análisis exploratorio de la base de datos para identificar respuestas incoherentes o eliminar variables o datos que no sirvan para los propósitos del investigador. En esta sección utilizamos una base de datos (ficticia) que simula la información obtenida de varios hospitales de Estados Unidos sobre sus pacientes con cáncer. El objetivo es: 1) realizar un análisis exploratorio de la información y 2) limpiar la base de datos tal que sea posible hacer análisis estadísticos más profundos.

Durante toda la sección estaremos utilizando los paquetes tidyverse y Hmisc de R, los cuales contienen comandos que simplifican la exploración y limpieza de la base de datos.

# Instalar paquetes si es necesario
# install.packages("tidyverse")
# install.packages("Hmisc")

# Leer paquetes
library(tidyverse)
## ── Attaching packages ──────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
## ✓ tibble  3.0.1     ✓ dplyr   1.0.0
## ✓ tidyr   1.1.0     ✓ stringr 1.4.0
## ✓ readr   1.3.1     ✓ forcats 0.5.0
## ── Conflicts ─────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(Hmisc)
## Loading required package: lattice
## Loading required package: survival
## Loading required package: Formula
## 
## Attaching package: 'Hmisc'
## The following objects are masked from 'package:dplyr':
## 
##     src, summarize
## The following objects are masked from 'package:base':
## 
##     format.pval, units

Limpieza de la base de datos y valores ausentes

Comenzamos leyendo la base de datos. Esta consta de 24 variables y 120 observaciones que simulan información sobre pacientes con cáncer. El archivo está en formato de valores separados por comas, .csv. Puedes descargar el archivo al disco duro de tu computadora o bien puedes leer el archivo desde su ubicación en línea. El comando de lectura puede ser el preinstalado en R: read.cvs, o bien el comando contenido en el paquete tidyverse: read_csv. Aquí utilizaremos la segunda opción, pero asegúrate de comprender las diferencias entre ambos comandos utilizando el símbolo de ayuda (por ejemplo: ?read_csv).

db <- read_csv("https://raw.githubusercontent.com/amosino/courses--econometria/master/econometria_salud/econometria_salud--datos/CancerData_P1.csv")
## Parsed with column specification:
## cols(
##   .default = col_double(),
##   hospital = col_character(),
##   docid = col_character(),
##   dis_date = col_character(),
##   sex = col_character(),
##   familyhx = col_character(),
##   smokinghx = col_character(),
##   cancerstage = col_character(),
##   wbc = col_character()
## )
## See spec(...) for full column specifications.
db
## # A tibble: 120 x 24
##    hospital hospid docid dis_date sex     age   test1 test2  pain tumorsize
##    <chr>     <dbl> <chr> <chr>    <chr> <dbl>   <dbl> <dbl> <dbl>     <dbl>
##  1 UCLA          1 1-1   6-Sep-09 male   65.0   3.70  8.09      4      68.0
##  2 UCLA          1 1-1   7-Jan-11 fema…  53.9   2.63  0.803     2      64.7
##  3 UCLA          1 1-1   4-Sep-10 male   41.4 -99     2.13      3      86.4
##  4 UCLA          1 1-1   25-Jun-… male   46.8   3.89  1.35      3      53.4
##  5 UCLA          1 1-1   1-Jul-09 male   51.9   1.42  2.19      4      51.7
##  6 UCLA          1 1-1   6-Mar-09 fema…  53.8   2.29  8.61      3      78.9
##  7 UCLA          1 1-1   15-Apr-… male   54.4   8.03  7.23      4      62.9
##  8 UCLA          1 1-11  12-Jul-… fema…  47.1   0.810 2.60      5      81.2
##  9 UCLA          1 1-11  25-Jul-… fema…  59.3 -99     5.18      4      73.2
## 10 UCLA          1 1-11  12-Jul-… fema…  47.1   0.810 2.60      5      81.2
## # … with 110 more rows, and 14 more variables: co2 <dbl>, wound <dbl>,
## #   mobility <dbl>, ntumors <dbl>, remission <dbl>, lungcapacity <dbl>,
## #   married <dbl>, familyhx <chr>, smokinghx <chr>, cancerstage <chr>,
## #   lengthofstay <dbl>, wbc <chr>, rbc <dbl>, bmi <dbl>

Como primer paso, conviene hacer un análisis descriptivo de la base de datos. Esto nos permitirá no solo conocer la distribución de la muestra, si no que también nos permite detectar valores sospechosos. Para obtener los estadísticos básicos de la muestra, tenemos dos opciones. Una es utilizar la función summary(), la cual viene preinstalada en R. La otra opción es el comando describe(), el cual es parte del paquete Hmisc. Aquí preferimos la segunda opción, ya que esta nos da tablas de frecuencias para las variables discretas (que tengan menos de 20 valores), más detalle sobre los cuantiles para las variables continuas, conteos y proporciones para las variables binarias, y conteo de datos faltantes (NA).

describe(db)
## db 
## 
##  24  Variables      120  Observations
## --------------------------------------------------------------------------------
## hospital 
##        n  missing distinct 
##      120        0        2 
##                       
## Value       UCLA  UCSF
## Frequency     62    58
## Proportion 0.517 0.483
## --------------------------------------------------------------------------------
## hospid 
##        n  missing distinct     Info     Mean      Gmd 
##      120        0        2    0.749    1.483   0.5036 
##                       
## Value          1     2
## Frequency     62    58
## Proportion 0.517 0.483
## --------------------------------------------------------------------------------
## docid 
##        n  missing distinct 
##      120        0       22 
## 
## lowest : 1-1   1-100 1-11  1-21  1-22 , highest: 2-177 2-178 2-188 2-201 2-216
## --------------------------------------------------------------------------------
## dis_date 
##        n  missing distinct 
##      120        0      104 
## 
## lowest : 1-Jul-09  10-Jun-09 10-Jun-10 11-Apr-10 11-Dec-09
## highest: 9-Apr-10  9-Feb-09  9-Feb-10  9-Jun-10  9-May-10 
## --------------------------------------------------------------------------------
## sex 
##        n  missing distinct 
##      120        0        3 
##                                
## Value        12.2 female   male
## Frequency       1     74     45
## Proportion  0.008  0.617  0.375
## --------------------------------------------------------------------------------
## age 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      117        1    53.59    11.92    40.86    41.52 
##      .25      .50      .75      .90      .95 
##    47.75    51.83    55.01    58.61    59.65 
## 
## lowest :  34.19229  35.31930  37.25225  39.61641  40.03724
## highest:  63.93238  64.16432  64.96824  65.80417 357.89001
##                                                           
## Value         35    40    45    50    55    60    65   360
## Frequency      3    10    16    38    35    13     4     1
## Proportion 0.025 0.083 0.133 0.317 0.292 0.108 0.033 0.008
## 
## For the frequency table, variable is rounded to the nearest 5
## --------------------------------------------------------------------------------
## test1 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      111        1   -1.989    14.11 -99.0000   0.5809 
##      .25      .50      .75      .90      .95 
##   1.5597   3.1065   5.7067   7.9931   9.5583 
## 
## lowest : -99.0000000   0.1048958   0.1927608   0.4293420   0.5155185
## highest:   9.7981329  10.2903990  10.4685400  11.0714440  12.4163920
##                                                                             
## Value        -99     0     1     2     3     4     5     6     7     8     9
## Frequency      7     3    20    17    21    12     8    11     6     7     1
## Proportion 0.058 0.025 0.167 0.142 0.175 0.100 0.067 0.092 0.050 0.058 0.008
##                             
## Value         10    11    12
## Frequency      5     1     1
## Proportion 0.042 0.008 0.008
## 
## For the frequency table, variable is rounded to the nearest 1
## --------------------------------------------------------------------------------
## test2 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      111        1   -1.226    14.59 -99.0000   0.7881 
##      .25      .50      .75      .90      .95 
##   2.2494   4.1620   6.1657   8.9973  10.9861 
## 
## lowest : -99.0000000   0.5807591   0.6179262   0.6571499   0.7485080
## highest:  11.4315750  12.4493730  14.2252270  14.5365420  17.2275810
##                                                                             
## Value        -99     1     2     3     4     5     6     7     8     9    10
## Frequency      7    15    12    19    16    15     9     6     6     6     1
## Proportion 0.058 0.125 0.100 0.158 0.133 0.125 0.075 0.050 0.050 0.050 0.008
##                                         
## Value         11    12    14    15    17
## Frequency      4     1     1     1     1
## Proportion 0.033 0.008 0.008 0.008 0.008
## 
## For the frequency table, variable is rounded to the nearest 1
## --------------------------------------------------------------------------------
## pain 
##        n  missing distinct     Info     Mean      Gmd 
##      120        0        9    0.961    5.325    1.742 
## 
## lowest : 1 2 3 4 5, highest: 5 6 7 8 9
##                                                                 
## Value          1     2     3     4     5     6     7     8     9
## Frequency      1     2    11    21    33    25    18     5     4
## Proportion 0.008 0.017 0.092 0.175 0.275 0.208 0.150 0.042 0.033
## --------------------------------------------------------------------------------
## tumorsize 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      117        1    70.08    12.92    53.48    55.97 
##      .25      .50      .75      .90      .95 
##    62.18    68.23    77.48    84.06    92.54 
## 
## lowest :  49.09861  50.28009  50.50217  51.65727  53.19937
## highest:  98.05510  98.32850  98.70570 102.69671 109.00956
## --------------------------------------------------------------------------------
## co2 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      115        1  -0.8901     5.03    1.390    1.440 
##      .25      .50      .75      .90      .95 
##    1.495    1.586    1.686    1.756    1.793 
## 
## lowest : -98.000000   1.327440   1.362927   1.372113   1.391310
## highest:   1.810847   1.820266   1.905606   1.920025   1.942401
##                             
## Value        -98     1     2
## Frequency      3    29    88
## Proportion 0.025 0.242 0.733
## 
## For the frequency table, variable is rounded to the nearest 1
## --------------------------------------------------------------------------------
## wound 
##        n  missing distinct     Info     Mean      Gmd 
##      120        0        8    0.958    5.592    1.767 
## 
## lowest : 2 3 4 5 6, highest: 5 6 7 8 9
##                                                           
## Value          2     3     4     5     6     7     8     9
## Frequency      5    10    12    23    36    22    10     2
## Proportion 0.042 0.083 0.100 0.192 0.300 0.183 0.083 0.017
## --------------------------------------------------------------------------------
## mobility 
##        n  missing distinct     Info     Mean      Gmd 
##      120        0        8    0.969    6.033    2.264 
## 
## lowest : 2 3 4 5 6, highest: 5 6 7 8 9
##                                                           
## Value          2     3     4     5     6     7     8     9
## Frequency      6     4    19    19    28    15     5    24
## Proportion 0.050 0.033 0.158 0.158 0.233 0.125 0.042 0.200
## --------------------------------------------------------------------------------
## ntumors 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0       10    0.981     3.15    3.101        0        0 
##      .25      .50      .75      .90      .95 
##        1        2        5        8        9 
## 
## lowest : 0 1 2 3 4, highest: 5 6 7 8 9
##                                                                       
## Value          0     1     2     3     4     5     6     7     8     9
## Frequency     23    21    19     9    12    11     7     5     5     8
## Proportion 0.192 0.175 0.158 0.075 0.100 0.092 0.058 0.042 0.042 0.067
## --------------------------------------------------------------------------------
## remission 
##        n  missing distinct     Info      Sum     Mean      Gmd 
##      120        0        2    0.683       42     0.35   0.4588 
## 
## --------------------------------------------------------------------------------
## lungcapacity 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0       96    0.995   -18.33    31.26 -99.0000 -99.0000 
##      .25      .50      .75      .90      .95 
##   0.5142   0.7457   0.8721   0.9573   0.9790 
## 
## lowest : -99.0000000 -98.0000000   0.2949074   0.3264440   0.3450253
## highest:   0.9856416   0.9864109   0.9924814   0.9940955   0.9982018
##                                                     
## Value      -99.0 -98.0   0.2   0.4   0.6   0.8   1.0
## Frequency     20     3     1     6    21    46    23
## Proportion 0.167 0.025 0.008 0.050 0.175 0.383 0.192
## 
## For the frequency table, variable is rounded to the nearest 0.2
## --------------------------------------------------------------------------------
## married 
##        n  missing distinct     Info      Sum     Mean      Gmd 
##      120        0        2    0.697       76   0.6333   0.4683 
## 
## --------------------------------------------------------------------------------
## familyhx 
##        n  missing distinct 
##      120        0        3 
##                             
## Value        -99    no   yes
## Frequency      6    97    17
## Proportion 0.050 0.808 0.142
## --------------------------------------------------------------------------------
## smokinghx 
##        n  missing distinct 
##      120        0        4 
##                                           
## Value          -99 current  former   never
## Frequency        6      26      22      66
## Proportion   0.050   0.217   0.183   0.550
## --------------------------------------------------------------------------------
## cancerstage 
##        n  missing distinct 
##      120        0        4 
##                                   
## Value          I    II   III    IV
## Frequency     40    54    17     9
## Proportion 0.333 0.450 0.142 0.075
## --------------------------------------------------------------------------------
## lengthofstay 
##        n  missing distinct     Info     Mean      Gmd 
##      120        0        6    0.933    5.308    1.271 
## 
## lowest : 3 4 5 6 7, highest: 4 5 6 7 8
##                                               
## Value          3     4     5     6     7     8
## Frequency      5    29    29    40    15     2
## Proportion 0.042 0.242 0.242 0.333 0.125 0.017
## --------------------------------------------------------------------------------
## wbc 
##        n  missing distinct 
##      120        0      116 
## 
## lowest : 3671.880371  4176.054199  4201.741211  4238.355957  4331.902344 
## highest: 7999.091309  8340.71582   8415.605469  8567.246094  not assessed
## --------------------------------------------------------------------------------
## rbc 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      117        1     4.97   0.2885    4.526    4.606 
##      .25      .50      .75      .90      .95 
##    4.825    4.978    5.150    5.285    5.353 
## 
## lowest : 4.359662 4.436482 4.456108 4.465468 4.470100
## highest: 5.441604 5.442614 5.459067 5.502604 5.535052
## --------------------------------------------------------------------------------
## bmi 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      117        1    29.38    7.278    20.71    22.07 
##      .25      .50      .75      .90      .95 
##    24.51    27.82    34.31    37.75    40.71 
## 
## lowest : 18.44992 18.68505 20.07485 20.17994 20.49195
## highest: 42.84858 44.04223 46.50746 52.30723 58.00000
## --------------------------------------------------------------------------------

A simple vista podemos detectar algunos valores sospechosos. Por ejemplo:

describe(db[,c("age", "sex", "test1")])
## db[, c("age", "sex", "test1")] 
## 
##  3  Variables      120  Observations
## --------------------------------------------------------------------------------
## age 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      117        1    53.59    11.92    40.86    41.52 
##      .25      .50      .75      .90      .95 
##    47.75    51.83    55.01    58.61    59.65 
## 
## lowest :  34.19229  35.31930  37.25225  39.61641  40.03724
## highest:  63.93238  64.16432  64.96824  65.80417 357.89001
##                                                           
## Value         35    40    45    50    55    60    65   360
## Frequency      3    10    16    38    35    13     4     1
## Proportion 0.025 0.083 0.133 0.317 0.292 0.108 0.033 0.008
## 
## For the frequency table, variable is rounded to the nearest 5
## --------------------------------------------------------------------------------
## sex 
##        n  missing distinct 
##      120        0        3 
##                                
## Value        12.2 female   male
## Frequency       1     74     45
## Proportion  0.008  0.617  0.375
## --------------------------------------------------------------------------------
## test1 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##      120        0      111        1   -1.989    14.11 -99.0000   0.5809 
##      .25      .50      .75      .90      .95 
##   1.5597   3.1065   5.7067   7.9931   9.5583 
## 
## lowest : -99.0000000   0.1048958   0.1927608   0.4293420   0.5155185
## highest:   9.7981329  10.2903990  10.4685400  11.0714440  12.4163920
##                                                                             
## Value        -99     0     1     2     3     4     5     6     7     8     9
## Frequency      7     3    20    17    21    12     8    11     6     7     1
## Proportion 0.058 0.025 0.167 0.142 0.175 0.100 0.067 0.092 0.050 0.058 0.008
##                             
## Value         10    11    12
## Frequency      5     1     1
## Proportion 0.042 0.008 0.008
## 
## For the frequency table, variable is rounded to the nearest 1
## --------------------------------------------------------------------------------

Otra de las ventajas que tiene el comando describe(), es que este se puede graficar con el comando plot(). Este comando aplicado a describe() resulta en gráficas de puntos para las variables categóricas (o discretas) e histogramas para las variables continuas. Además, las gráficas incluyen un indicador de color para mostrar el número de NA que contiene cada variable.

plot(describe(db))
## $Categorical

## 
## $Continuous

Normalmente, las gráficas tienden a revelar más información sobre los valores sospechosos que las tablas de datos. Sin embargo, ambas revelan la misma información. En nuestro caso:

  • La variable edad tiene un valor máximo de 357.89001.
  • La varible sex muestra una categoría “12.2”.
  • Algunas variables, como test1, test2, lungcapacity, entre otras tienen valores de -99 (el cual, usualmente, corresponde a “no sabe” o “ausente”).
  • Algunas variables, como co2, y lungcapacity tienen valores de -98 (el cual, usualmente, corresponde a “no quiso contestar”).
  • wbc parece ser una variable numérica, pero, debido a que tiene valores “not assesed”, R la interpreta como si se tratase de una cadena de caracteres.

Para que la base de datos sea utilizable, podemos reemplazar todos estos valores sospechosos por NA. Por supuesto que también es posible reemplazar los valores sopechosos con sus valores verdaderos, pero esto normalmente no es posible porque esta es información a la que normalmente no tenemos acceso. La siguiente secuencia de comandos haría el trabajo:

db$age[db$age<18|db$age>120] <- NA
db$sex[db$sex == "12.2"] <- NA
db[db==-99] <- NA
db[db==-98] <- NA
db[db=="not assesed"] <- NA

Volvemos a utilizar los comandos describe() y plot() para ver cómo ha cambiado la gráfica descriptiva. Mucho mejor, ¿no?

plot(describe(db))
## $Categorical

## 
## $Continuous

Otros comandos útiles

Otros comandos que pueden ayudarnos a limpiar la base de datos son: filter(), select(), arrange(), mutate() y summarize(), los cuales son parte del paquete tidyverse. El uso de estos comandos tiene algunas ventajas. Especificamente, estas funciones simplifican el código, ya que solo hay que nombrar la base de datos una vez y proporcionar las variables como argumentos. Además, las variables no tienen que escribirse entre comillas.

filter()

El comando filter() nos permite elegir una submuestra usando operadores lógicos. Estos operadores pueden ser:

  • ==: Igualdad.
  • !=: Desigualdad.
  • >, >=: Mayor que o mayor o igual que.
  • <, <=: Menor que o menor o igual que.
  • &: Y.
  • |: O.
  • %in%: En.
  • is.na: es igual a NA.
  • near(): Igualdad para un valor con decimales. Puede modificarse la tolerancia.

Un ejemplo de uso para el comando filter() es elegir todas las personas de género femenino con un grado de dolor mayor o igual a 9:

dfhp <- filter(db, sex=="female", pain>=9)

¿Cómo encontrarías este subgrupo de pacientes sin utilizar filter()?

select()

El comando select() se utiliza para mantener solo las variables que nos interesan. Por ejemplo, si deseamos mantener solo las variables hospid, docid, age y cancerstage:

db_small <- select(db, hospid, docid, age, cancerstage)
db_small
## # A tibble: 120 x 4
##    hospid docid   age cancerstage
##     <dbl> <chr> <dbl> <chr>      
##  1      1 1-1    65.0 II         
##  2      1 1-1    53.9 II         
##  3      1 1-1    41.4 I          
##  4      1 1-1    46.8 II         
##  5      1 1-1    51.9 I          
##  6      1 1-1    53.8 II         
##  7      1 1-1    54.4 II         
##  8      1 1-11   47.1 I          
##  9      1 1-11   59.3 IV         
## 10      1 1-11   47.1 I          
## # … with 110 more rows

También podemos seleccionar un grupo de variables con nombres similares. Esto se hace con las funciones:

  • starts_with(x): Comienza con.
  • ends_with(x): Termina con.
  • contains(x): Contiene.
  • matches(re): Encuentra la expresión regulares re.
  • num_range(prefijo, rango): Encuentra todos los nombres de variables que contengan prefijo y un elemento de rango.

Por ejemplo: seleccionar todas las variables que comiencen con hosp y obtener las variables test1 y test2 usando num_range().

# Obtener las variables que comiencen con hosp
dbhosp <- select(db, starts_with("hosp"))
names(dbhosp)
## [1] "hospital" "hospid"
# Obtener las variables test1 y tes2
dbhosp <- select(db, num_range("test", 1:2))
names(dbhosp)
## [1] "test1" "test2"

arrange()

El comando arrange() nos permite ordenar las filas de una base de datos de acuerdo a los criterios especificados por el usuario. El comando arrange() ordena automáticamente en orden ascendente, pero se puede ordenar el orden descendente si utilizamos el argumento desc. Por ejemplo, podemos ordenar nuestra base de datos primero hombres, luego mujeres y luego por edades, primero lo más jóvenes:

arrange(db, desc(sex), age)
## # A tibble: 120 x 24
##    hospital hospid docid dis_date sex     age test1  test2  pain tumorsize   co2
##    <chr>     <dbl> <chr> <chr>    <chr> <dbl> <dbl>  <dbl> <dbl>     <dbl> <dbl>
##  1 UCSF          2 2-178 19-Apr-… male   34.2  4.63  3.26      3      65.5  1.64
##  2 UCLA          1 1-58  9-Feb-10 male   35.3  2.14 12.4       6      60.5  1.56
##  3 UCSF          2 2-121 20-Apr-… male   40.1 NA    NA         5      73.5  1.65
##  4 UCLA          1 1-1   4-Sep-10 male   41.4 NA     2.13      3      86.4  1.45
##  5 UCSF          2 2-121 22-Aug-… male   41.5  2.88  6.80      5      60.2  1.50
##  6 UCSF          2 2-163 19-Apr-… male   42.5  1.63  0.749     4      54.0  1.72
##  7 UCLA          1 1-11  19-Aug-… male   43.7 10.5   5.89      7      61.3  1.49
##  8 UCLA          1 1-1   25-Jun-… male   46.8  3.89  1.35      3      53.4  1.57
##  9 UCLA          1 1-21  22-Apr-… male   48.0  3.39 NA         8      79.6  1.55
## 10 UCSF          2 2-178 12-Mar-… male   48.9  4.15  3.04      7      71.8  1.47
## # … with 110 more rows, and 13 more variables: wound <dbl>, mobility <dbl>,
## #   ntumors <dbl>, remission <dbl>, lungcapacity <dbl>, married <dbl>,
## #   familyhx <chr>, smokinghx <chr>, cancerstage <chr>, lengthofstay <dbl>,
## #   wbc <chr>, rbc <dbl>, bmi <dbl>

mutate()

El comando mutate() nos permite transformar muchas variables en un solo paso sin tener que espcificar una y otra vez el nombre de la base de datos a la que pertenecen. Algunas de las transformaciones que pueden hacerse en R son:

  • log(): Logaritmo.
  • cut(): Corta una variable continua en intervalos.
  • scale(): Estandariza una variable (resta la media y divide entre su desviación estándar.
  • lag() y lead(): Rezaga o adelanta una variable.
  • cumsum(): Suma acumulada.
  • rowMeans() y rowSums(): Promedios y sumas de varias columnas.
  • recode(): Recodifica valores.

Como ejemplo, supongamos que deseamos crear una variable de edad, agecat, por intervalos de 10 años y una variable binaria, highpain, que indique las personas que tienen más dolor que el promedio:

db <- mutate(db, agecat=cut(age, breaks=c(30,40,50,60,70,120)), highpain=pain>mean(pain))
table(db$agecat, db$highpain)
##           
##            FALSE TRUE
##   (30,40]      2    2
##   (40,50]     27   15
##   (50,60]     37   32
##   (60,70]      2    2
##   (70,120]     0    0

Unir y combinar bases de datos

En ocasiones las bases de datos están divididas en varias partes. Cada parte puede contener las mismas variables para nuevos individuos, o cada parte puede contener nuevas variables para los mismos individuos. En el primer caso, lo que tenemos que hacer es unir las diferentes partes de las bases de datos, y en el segundo lo que tenemos que hacer es combinarlas.

Unir bases de datos

Considera una segunda parte de la base de datos que hemos considerado hasta ahora. Tómate el tiempo para analizarla. Nota que esta parte contiene prácticamente las mismas variables que la base de datos original, pero contiene nuevos individuos. Las diferencias entre las dos bases de datos resultan de las manipulaciones que hemos hecho arriba a la base de datos original.

db2 <- read_csv("https://raw.githubusercontent.com/amosino/courses--econometria/master/econometria_salud/econometria_salud--datos/CancerData_P2.csv")
## Parsed with column specification:
## cols(
##   .default = col_double(),
##   hospital = col_character(),
##   docid = col_character(),
##   dis_date = col_character(),
##   sex = col_character(),
##   familyhx = col_character(),
##   smokinghx = col_character(),
##   cancerstage = col_character(),
##   wbc = col_character()
## )
## See spec(...) for full column specifications.
db2
## # A tibble: 111 x 24
##    hospital hospid docid dis_date sex     age test1  test2  pain tumorsize
##    <chr>     <dbl> <chr> <chr>    <chr> <dbl> <dbl>  <dbl> <dbl>     <dbl>
##  1 Cedars-…      3 3-227 1-Oct-09 male   54.2 3.87    1.37     6      69.8
##  2 Cedars-…      3 3-227 18-Feb-… fema…  55.1 7.76    4.50     8      68.0
##  3 Cedars-…      3 3-227 30-Jun-… fema…  58.1 5.73  -99        7      65.1
##  4 Cedars-…      3 3-227 15-Nov-… fema…  51.5 8.58    4.61     9      71.4
##  5 Cedars-…      3 3-227 17-Feb-… fema…  54.6 6.70    2.64     4      69.5
##  6 Cedars-…      3 3-227 22-Dec-… male   49.1 2.87    5.46     5      89.7
##  7 Cedars-…      3 3-227 15-Apr-… fema…  59.8 9.60    3.37     6      73.1
##  8 Cedars-…      3 3-241 17-Apr-… fema…  49.7 0.175   8.69     3      80.6
##  9 Cedars-…      3 3-241 12-Oct-… male   65.6 6.08    6.79     5      55.1
## 10 Cedars-…      3 3-241 30-Oct-… male   57.4 6.21   14.0      8      61.3
## # … with 101 more rows, and 14 more variables: co2 <dbl>, wound <dbl>,
## #   mobility <dbl>, ntumors <dbl>, remission <dbl>, lungcapacity <dbl>,
## #   married <dbl>, familyhx <chr>, smokinghx <chr>, cancerstage <chr>,
## #   lengthofstay <dbl>, wbc <chr>, rbc <dbl>, bmi <dbl>

El objetivo es unir las bases de datos. Para esto podemos utilizar el comando preinstalado rbind(), o bien el comando bind_rowns() que es parte del paquete tidyverse. Existen algunas diferencias entre ambos métodos. Por ejemplo, si en una de las bases de datos que vamos a unir existen columnas que en la otra no rbind() resultará en un error, mientras que bind_rows() creará las nuevas columnas y rellenará los datos faltantes con NA. Otra diferencia es que si la misma columna en ambas bases de datos contiene datos de diferentes tipos (por ejemplo, en una base de datos son caracteres y en la otra son datos numéricos), rbind() convertirá los datos de la base de datos unificada en datos del mismo tipo siguiendo el orden: datos lógicos, datos numéricos enteros, datos numéricos con decimales, y, finalmente, caracteres; en cambio bind_rows() resultará en un error. Aquí utilizamos el comando bind_rows(), pero explora cómo hacer la unión de las bases usando rbind().

db_union <- bind_rows(db, db2, .id="source")
db_union
## # A tibble: 231 x 27
##    source hospital hospid docid dis_date sex     age  test1 test2  pain
##    <chr>  <chr>     <dbl> <chr> <chr>    <chr> <dbl>  <dbl> <dbl> <dbl>
##  1 1      UCLA          1 1-1   6-Sep-09 male   65.0  3.70  8.09      4
##  2 1      UCLA          1 1-1   7-Jan-11 fema…  53.9  2.63  0.803     2
##  3 1      UCLA          1 1-1   4-Sep-10 male   41.4 NA     2.13      3
##  4 1      UCLA          1 1-1   25-Jun-… male   46.8  3.89  1.35      3
##  5 1      UCLA          1 1-1   1-Jul-09 male   51.9  1.42  2.19      4
##  6 1      UCLA          1 1-1   6-Mar-09 fema…  53.8  2.29  8.61      3
##  7 1      UCLA          1 1-1   15-Apr-… male   54.4  8.03  7.23      4
##  8 1      UCLA          1 1-11  12-Jul-… fema…  47.1  0.810 2.60      5
##  9 1      UCLA          1 1-11  25-Jul-… fema…  59.3 NA     5.18      4
## 10 1      UCLA          1 1-11  12-Jul-… fema…  47.1  0.810 2.60      5
## # … with 221 more rows, and 17 more variables: tumorsize <dbl>, co2 <dbl>,
## #   wound <dbl>, mobility <dbl>, ntumors <dbl>, remission <dbl>,
## #   lungcapacity <dbl>, married <dbl>, familyhx <chr>, smokinghx <chr>,
## #   cancerstage <chr>, lengthofstay <dbl>, wbc <chr>, rbc <dbl>, bmi <dbl>,
## #   agecat <fct>, highpain <lgl>

Nota que en la unión hemos utilizado el argumento .id="source". Este creará una nueva columna al inicio de la base de datos que nos indica el origen de cada fila. En este caso, un valor de 1 indica que esa observación proviene de la primer base de datos (db) y un valor de 2 de la segunda base de datos (db2).

Combinar bases de datos

Ahora supongamos que una tercera base de datos contiene nuevas variables, pero (virtualmente) los mismos individuos. En este caso, las bases de datos pueden combinarse si existe alguna o algunas variales que identifiquen a los individuos.

db3 <- read_csv("https://raw.githubusercontent.com/amosino/courses--econometria/master/econometria_salud/econometria_salud--datos/CancerData_D1.csv")
## Parsed with column specification:
## cols(
##   docid = col_character(),
##   experience = col_double(),
##   school = col_character(),
##   lawsuits = col_double(),
##   medicaid = col_double()
## )
db3
## # A tibble: 40 x 5
##    docid experience school  lawsuits medicaid
##    <chr>      <dbl> <chr>      <dbl>    <dbl>
##  1 1-1           25 average        3    0.606
##  2 1-11          10 top            0    0.605
##  3 1-21          21 average        3    0.483
##  4 1-22          22 top            3    0.483
##  5 1-33          16 top            0    0.584
##  6 1-48          23 average        3    0.219
##  7 1-57          21 average        1    0.405
##  8 1-58          21 average        1    0.405
##  9 1-72          24 average        4    0.522
## 10 1-73          14 average        1    0.522
## # … with 30 more rows

Al igual que en la sección anterior, existen al menos dos métodos para combinar las bases de datos en R. Una de ellas es utilizar el comando preinstalado merge() o bien el comando dplyr() del paquete tidyverse. En realidad no existen diferencias importantes entre ambas opciones, aunque merge() tiende a ser un poco más lento computacionalmente que dplyr(). Este último tiene tres diferentes versiones:

  • inner_join(x,y): regresa todas las filas de la base de datos x que coinciden con las filas de la base de datos y.
  • left_join(x,y): regresa todas las filas de la base de datos x. Las filas de x que no coincidan con las filas de la base de datos y se remplazan con NA. Las filas de la base de datos y que no coincidan con las de la base x no se consideran.
  • full_join(x,y): regresa todas las filas de la base de datos x y todas las filas de la base de datos y. Las filas de x que no coincidan con las filas de la base de datos y y las filas de y que no coincidan con la base de datos x se remplazan con NA.

Tómate el tiempo para explorar las tres posibilidades. Aquí utilizamos full_join() para mostrar el uso de este comando. Nota que R elige la variable docid para combinar las bases de datos de manera automática. Sin embargo, también es posible utilizar el argumento by para elegir la variable para combinar las bases de forma “manual”.

db_full <- full_join(db_union, db3)
## Joining, by = "docid"
db_full
## # A tibble: 233 x 31
##    source hospital hospid docid dis_date sex     age  test1 test2  pain
##    <chr>  <chr>     <dbl> <chr> <chr>    <chr> <dbl>  <dbl> <dbl> <dbl>
##  1 1      UCLA          1 1-1   6-Sep-09 male   65.0  3.70  8.09      4
##  2 1      UCLA          1 1-1   7-Jan-11 fema…  53.9  2.63  0.803     2
##  3 1      UCLA          1 1-1   4-Sep-10 male   41.4 NA     2.13      3
##  4 1      UCLA          1 1-1   25-Jun-… male   46.8  3.89  1.35      3
##  5 1      UCLA          1 1-1   1-Jul-09 male   51.9  1.42  2.19      4
##  6 1      UCLA          1 1-1   6-Mar-09 fema…  53.8  2.29  8.61      3
##  7 1      UCLA          1 1-1   15-Apr-… male   54.4  8.03  7.23      4
##  8 1      UCLA          1 1-11  12-Jul-… fema…  47.1  0.810 2.60      5
##  9 1      UCLA          1 1-11  25-Jul-… fema…  59.3 NA     5.18      4
## 10 1      UCLA          1 1-11  12-Jul-… fema…  47.1  0.810 2.60      5
## # … with 223 more rows, and 21 more variables: tumorsize <dbl>, co2 <dbl>,
## #   wound <dbl>, mobility <dbl>, ntumors <dbl>, remission <dbl>,
## #   lungcapacity <dbl>, married <dbl>, familyhx <chr>, smokinghx <chr>,
## #   cancerstage <chr>, lengthofstay <dbl>, wbc <chr>, rbc <dbl>, bmi <dbl>,
## #   agecat <fct>, highpain <lgl>, experience <dbl>, school <chr>,
## #   lawsuits <dbl>, medicaid <dbl>

Otros problemas con las bases de datos

Para terminar, veremos algunos otros problemas que podríamos enfrentar cuando analizamos bases de datos. Por ejemplo, quizás sea necesario reestructurar la base de datos de tal forma que esta pueda utilizarse para realizar un análisis estadístico o econométrico. También suele ser muy frecuente encontrar múltiples variables en la misma celda, o bien el valor de alguna variable separado en varias celdas. El objetivo de esta sección es tratar estos casos.

gather()

Considera la siguiente base de datos (ficticia), que contiene el número de egresados por año en diferentes departamentos universitarios:

db_dept <- read_csv("https://raw.githubusercontent.com/amosino/courses--econometria/master/econometria_salud/econometria_salud--datos/DeptsData.csv")
## Parsed with column specification:
## cols(
##   id = col_character(),
##   `2015` = col_double(),
##   `2016` = col_double(),
##   `2017` = col_double()
## )
db_dept
## # A tibble: 3 x 4
##   id      `2015` `2016` `2017`
##   <chr>    <dbl>  <dbl>  <dbl>
## 1 biology    207    211    259
## 2 math        96     75     99
## 3 physics    112    126    125

Supongamos que nuestro interés es saber si el índice de egreso ha crecido a través del tiempo. Nota que el año debería ser la variable explicativa en nuestro análisis. Sin embargo, la base de datos la incluye como nombre de las variables. Para casos como este, el comando gather() nos permite restructurar la base de datos de tal forma que:

  • Una de las columnas de la base reestructurada se forme a partir de los nombres actuales de las variables. El nombre de esta columna se elige con el argumento key.
  • Los valores de las columnas originales se apilen en una sola columna. El nombre de esta columna se elige con el argumento value.

El comando gather() apila todas las columnas de la base de datos de manera automática. Sin embargo, existen algunas columnas que no cambian con los valores en la nueva columna con el nombre key; estas no deberían apilarse. Este es el caso, por ejemplo, de la actual variable id, el nombre de los departamentos no cambia con el año analizado. Para especificar cuáles variables cambian con key podemos ya sea espcificar cuáles son las columnas que deben apilarse, o bien especificar cuáles columnas no deben apilarse utilizando el símbolo -.

db_deptap <- gather(db_dept, key="year", value="grad", -id)
db_deptap
## # A tibble: 9 x 3
##   id      year   grad
##   <chr>   <chr> <dbl>
## 1 biology 2015    207
## 2 math    2015     96
## 3 physics 2015    112
## 4 biology 2016    211
## 5 math    2016     75
## 6 physics 2016    126
## 7 biology 2017    259
## 8 math    2017     99
## 9 physics 2017    125

¿Qué hubiera pasado si no usamos el argumento -id?

spread()

Ahora considera la siguiente base de datos, que contiene información sobre diferentes tipos de gusanos clasificados por edad, tamaño y peso. Estas características, sin embargo, están apiladas en la misma columna:

db_worms <- read_csv("https://raw.githubusercontent.com/amosino/courses--econometria/master/econometria_salud/econometria_salud--datos/WormsData.csv")
## Parsed with column specification:
## cols(
##   worm = col_double(),
##   feature = col_character(),
##   measure = col_double()
## )
db_worms
## # A tibble: 9 x 3
##    worm feature measure
##   <dbl> <chr>     <dbl>
## 1     1 age         5  
## 2     1 length      3.2
## 3     1 weight      4.1
## 4     2 age         4  
## 5     2 length      2.6
## 6     2 weight      3.5
## 7     3 age         5  
## 8     3 length      3.6
## 9     3 weight      5.5

Supongamos que nuestro interés es analizar la relación que existe entre la edad, el peso y el tamaño del gusano. En este caso, necesitamos que cada una de estas características constituya una columna independiente. Esta es la función del comando spread(). Los argumentos del comando spread() son nuevamente keyy value. En este caso, key es la columna que será convertida en el nombre de las variables, y value es la columna de valores que será organizada en las nuevas columnas.

worms_spread <- spread(db_worms, key=feature, value=measure)
worms_spread
## # A tibble: 3 x 4
##    worm   age length weight
##   <dbl> <dbl>  <dbl>  <dbl>
## 1     1     5    3.2    4.1
## 2     2     4    2.6    3.5
## 3     3     5    3.6    5.5

separate() y unite()

Finalmente, considera la siguiente base de datos (automáticamente cargada con el paquete tidyverse):

db_table <- table5
db_table
## # A tibble: 6 x 4
##   country     century year  rate             
## * <chr>       <chr>   <chr> <chr>            
## 1 Afghanistan 19      99    745/19987071     
## 2 Afghanistan 20      00    2666/20595360    
## 3 Brazil      19      99    37737/172006362  
## 4 Brazil      20      00    80488/174504898  
## 5 China       19      99    212258/1272915272
## 6 China       20      00    213766/1280428583

La tabla de datos que hemos cargado tiene al menos dos defectos. Primero, la variable rate en realidad contiene dos valores: número de casos y población. Segundo, las columnas century y year constituyen el año. El primer problema se resuelve con el comando separate(), el segundo con el comando unite().

Como su nombre lo indica, el comando separate() divide una columna en varias. Sus argumentos son: 1) col, el nombre de la columna a dividir, 2) into, el nombre de las nuevas columnas, 3) sep, el caracter que indica dónde separar una columna y 4) remove, que indica si las columnas concatenadas serán eliminada o no de la base de datos (pre establecido en TRUE.)

table_final <- separate(db_table, col=rate, into=c("cases", "population"), sep="/")
table_final
## # A tibble: 6 x 5
##   country     century year  cases  population
##   <chr>       <chr>   <chr> <chr>  <chr>     
## 1 Afghanistan 19      99    745    19987071  
## 2 Afghanistan 20      00    2666   20595360  
## 3 Brazil      19      99    37737  172006362 
## 4 Brazil      20      00    80488  174504898 
## 5 China       19      99    212258 1272915272
## 6 China       20      00    213766 1280428583

El comando unite() literalmente une dos o más columnas. Sus argumentos son: 1) col, el nombre de la nueva columna, 2) la lista de columnas a unir, 3) sep, el caracter que indica cómo concatenar las columnas (pre establecido en "_".)

table_final <- unite(table_final, col=year, century, year, sep="")
table_final
## # A tibble: 6 x 4
##   country     year  cases  population
##   <chr>       <chr> <chr>  <chr>     
## 1 Afghanistan 1999  745    19987071  
## 2 Afghanistan 2000  2666   20595360  
## 3 Brazil      1999  37737  172006362 
## 4 Brazil      2000  80488  174504898 
## 5 China       1999  212258 1272915272
## 6 China       2000  213766 1280428583

Última actualización: 02-09-2020.