INTRODUCCIÓN: el paquete dplyr

El periódico NYTimes señaló en 2014 que “los científicos de datos dedican entre el 50 y el 80 % de su tiempo a la mundana tarea de recopilar, manipular y preparar datos, antes de aplicar técnicas estadísticas más sofisticadas”. Por tanto, es conveniente aprender a formatear los datos de partida para ahorrar tiempo y esfuerzo. Esta labor constituye un paso previo indispensable antes de someter nuestros datos a análisis más concienzudos. El paquete dplyr() es la herramienta más utilizada para esta labor en R.

¿Qué tiene de especial dplyr?

  • Comprende muchas funciones que realizan operaciones comunes de manipulación de datos, como aplicar filtros, seleccionar columnas específicas, ordenar datos, agregar o eliminar columnas y resumir datos. Es muy fácil de aprender y usar. También es fácil recordar estas funciones. Por ejemplo,filter() se usa para filtrar filas, como indica su nombre.

  • Las funciones dplyr se procesan más rápido que las funciones base R. También son más estables en la sintaxis y admiten mejor los conjuntos de datos.

  • Usa el mismo lenguaje que otros paquetes tidyverse, como ggplot2, lo cual facilita su uso.

Alguna recomendaciones

  • Valores perdidos. Afortunadamente, todas las funciones de agregación tienen un argumento na.rm que elimina los valores faltantes antes del cálculo.

  • Recuentos. Siempre que realices una agregación es una buena idea incluir un recuento (n()) o un recuento de valores no faltantes ( sum(!is.na(x))). De esa manera, puedes comprobar que no estás sacando conclusiones basadas en cantidades muy pequeñas de datos.

Argumentos

Utiliza una gramática basada en funciones (“verbos”) que ayudan en las tareas más comunes de manipulación de datos. Todas las funciones que discutiremos en este capítulo tienen en común una serie de argumentos. En particular:

  • El primer argumento es un conjunto de datos (data frame). Los data frames deben estar bien organizados/estructurados, es decir debe existir una observación por columna y, cada columna representar una variable, medida o característica de esa observación.

  • Los argumentos posteriores describen qué hacer con el data frame, utilizando los nombres de las variables (sin comillas). Los otros argumentos describen que hacer con el data frame especificado en el primer argumento, podemos referirnos a las columnas en el data frame directamente sin utilizar el operador $, es decir sólo con el nombre de la columna/variable.

  • El resultado es un nuevo data frame.

PREPARATIVOS INICIALES

Para

setwd("D:/G2040/borrame")

A través de R se pueden descargar archivos directamente de internet usando R con la función getURL() del paquete RCurl. Esta función simplemente crea un enlace al fichero, requiriendo como argumento la dirección de internet del archivo que queremos descargar.

download.file(url = "https://personales.unican.es/rasillad/docencia/g2040/tema_1/datos/base.csv", 
  destfile = "D:/G2040/borrame/base.csv")

Para importar los datos a R simplemente se recurre a la función read.csv().

base <- read.csv("D:/G2040/borrame/base.csv")

Se puede visualizar el contenido del data frame invocando simplemente el objeto.

base

También es posible echar un vistazo al conjunto de datos mediante la función View()

View(base)

Conviene a menudo conocer no sólo el nombre de las variables, sino su tipo.

str(base)

A partir de la función anterior, ¿qué tipo de variables aparecen en el objeto “base”?

A partir de este momento se puede proceder a la manipulación de este dataframe. Para ello es necesario activar el paquete dplyr().

library(dplyr)

SELECCIONAR VARIABLES (COLUMNAS) DE UN DATAFRAME (select())

La sintaxis general es la siguiente:

select(base,                                             # Dataframe
       POP1997)                                          # Variable

A partir de esta sintaxis básica es posible crear otro dataframe:

variable_seleccionada <- select(base,                                             # Dataframe
                                POP1997)                                          # Variable

variable_seleccionada

Existen múltiples posibilidades, de las que sólo se mostrarán algunas:

select(base, AREA:POP1997)
select(base, -POP1997)
select(base, -(AREA:POP1997))

Selecciona columnas cuyo nombre contiene una determinada cadena de caracteres (string).

select(base,starts_with("POP"))
select(base, ends_with("97"))

Selecciona columnas cuyo nombres contienen un string

select(base, contains("19"))
select(base, everything())

CAMBIAR EL NOMBRE DE LAS VARIABLES (rename())

La función rename() esta diseñada para facilitar el proceso de modificación del nombre de variables. Su sintaxsis general es: rename(span style=“color:blue;”>nombre_dataframe, nuevo_nombre_variable = nombre_antiguo_variable).

En el caso que los ocupa, el nombre original de las variables se puede obtener con la función names().

names(base)

A continuación, las variables NAME y CNTY_FIPS se cambiarán por nombre y FIPS.

rename(base, nombre = NAME, FIPS = CNTY_FIPS)

CALCULAR UNA NUEVA VARIABLE EN UN DATAFRAME (mutate())

Con la función mutate() se pueden transformar las variables en un data frame. A continuación, podemos calcular cómo ha variado la población de cada una de las entidades entre los años 1990 y 1997.

mutate(base, var_pob = (POP1997 - POP1990)/POP1990 * 100)

Es posible encadenar varias expresiones en una misma sentencia:

mutate(base, densidad1990 = POP1990/AREA, densidad1997 = POP1990/AREA)
##          NAME CNTY_FIPS      AREA POP1990 POP1997 densidad1990 densidad1997
## 1     Clatsop         7   8165371   33301   35287 4.078321e-03 4.078321e-03
## 2    Columbia         9   6998979   37557   43645 5.366068e-03 5.366068e-03
## 3    Umatilla        59  32882502   59249   65136 1.801840e-03 1.801840e-03
## 4     Wallowa        63  31726985    6911    7538 2.178272e-04 2.178272e-04
## 5      Morrow        49  20302669    7625    9408 3.755664e-04 3.755664e-04
## 6       Union        61  20357027   23598   25160 1.159207e-03 1.159207e-03
## 7     Gilliam        21  12416975    1717    1981 1.382784e-04 1.382784e-04
## 8   Tillamook        57  11156812   21570   24405 1.933348e-03 1.933348e-03
## 9  Washington        67    722111  311554  391928 4.314489e-01 4.314489e-01
## 10    Sherman        55   8220778    1918    1809 2.333113e-04 2.333113e-04
## 11  Multnomah        51   4814434  583887  629165 1.212784e-01 1.212784e-01
## 12 Hood River        27   5301756   16903   19674 3.188189e-03 3.188189e-03
## 13      Wasco        65  23998934   21683   23225 9.034985e-04 9.034985e-04
## 14  Clackamas         5  18820812  278850  329251 1.481605e-02 1.481605e-02
## 15    Yamhill        71   7128672   65551   79861 9.195401e-03 9.195401e-03
## 16     Marion        47  11863489  228483  264680 1.925934e-02 1.925934e-02
## 17      Baker         1  30732579   15317   16527 4.983962e-04 4.983962e-04
## 18       Polk        53   7476106   49541   59546 6.626578e-03 6.626578e-03
## 19    Wheeler        69  17141015    1396    1691 8.144208e-05 8.144208e-05
## 20    Lincoln        41  10063651   38889   45766 3.864303e-03 3.864303e-03
## 21      Grant        23  45067081    7853    7985 1.742514e-04 1.742514e-04
## 22  Jefferson        31  18002958   13676   16677 7.596529e-04 7.596529e-04
## 23       Linn        43  23204363   91227  103605 3.931459e-03 3.931459e-03
## 24     Benton         3    682247   70811   76605 1.037909e-01 1.037909e-01
## 25      Crook        13  29883668   14111   16891 4.721977e-04 4.721977e-04
## 26    Malheur        45  98839546   26038   28671 2.634371e-04 2.634371e-04
## 27  Deschutes        17  30510858   74958  101221 2.456765e-03 2.456765e-03
## 28       Lane        39  46274196  282912  309646 6.113818e-03 6.113818e-03
## 29     Harney        25 102459641    7060    7066 6.890518e-05 6.890518e-05
## 30    Douglas        19  50730226   94649  101921 1.865732e-03 1.865732e-03
## 31    Klamath        35  61226755   57702   63137 9.424311e-04 9.424311e-04
## 32       Lake        37  83569499    7186    7315 8.598831e-05 8.598831e-05
## 33       Coos        11  16264708   60273   63235 3.705754e-03 3.705754e-03
## 34    Jackson        29  28034331  146389  171236 5.221776e-03 5.221776e-03
## 35      Curry        15  16587075   19327   21230 1.165184e-03 1.165184e-03
## 36  Josephine        33  16439447   62649   73305 3.810895e-03 3.810895e-03

También es factible aplicar funciones de otros paquetes. Por ejemplo, si queremos calcular la distribución de frecuencias absolutas acumuladas de la variable POP1997 podríamos utilizar la función cumsum() que se encuentra en el paquete base:

mutate(base, freq.acumulative = cumsum(POP1997))
##          NAME CNTY_FIPS      AREA POP1990 POP1997 freq.acumulative
## 1     Clatsop         7   8165371   33301   35287            35287
## 2    Columbia         9   6998979   37557   43645            78932
## 3    Umatilla        59  32882502   59249   65136           144068
## 4     Wallowa        63  31726985    6911    7538           151606
## 5      Morrow        49  20302669    7625    9408           161014
## 6       Union        61  20357027   23598   25160           186174
## 7     Gilliam        21  12416975    1717    1981           188155
## 8   Tillamook        57  11156812   21570   24405           212560
## 9  Washington        67    722111  311554  391928           604488
## 10    Sherman        55   8220778    1918    1809           606297
## 11  Multnomah        51   4814434  583887  629165          1235462
## 12 Hood River        27   5301756   16903   19674          1255136
## 13      Wasco        65  23998934   21683   23225          1278361
## 14  Clackamas         5  18820812  278850  329251          1607612
## 15    Yamhill        71   7128672   65551   79861          1687473
## 16     Marion        47  11863489  228483  264680          1952153
## 17      Baker         1  30732579   15317   16527          1968680
## 18       Polk        53   7476106   49541   59546          2028226
## 19    Wheeler        69  17141015    1396    1691          2029917
## 20    Lincoln        41  10063651   38889   45766          2075683
## 21      Grant        23  45067081    7853    7985          2083668
## 22  Jefferson        31  18002958   13676   16677          2100345
## 23       Linn        43  23204363   91227  103605          2203950
## 24     Benton         3    682247   70811   76605          2280555
## 25      Crook        13  29883668   14111   16891          2297446
## 26    Malheur        45  98839546   26038   28671          2326117
## 27  Deschutes        17  30510858   74958  101221          2427338
## 28       Lane        39  46274196  282912  309646          2736984
## 29     Harney        25 102459641    7060    7066          2744050
## 30    Douglas        19  50730226   94649  101921          2845971
## 31    Klamath        35  61226755   57702   63137          2909108
## 32       Lake        37  83569499    7186    7315          2916423
## 33       Coos        11  16264708   60273   63235          2979658
## 34    Jackson        29  28034331  146389  171236          3150894
## 35      Curry        15  16587075   19327   21230          3172124
## 36  Josephine        33  16439447   62649   73305          3245429

CAMBIAR LAS POSICIONES DE LAS COLUMNS (relocate())

relocate(base, AREA)
##         AREA       NAME CNTY_FIPS POP1990 POP1997
## 1    8165371    Clatsop         7   33301   35287
## 2    6998979   Columbia         9   37557   43645
## 3   32882502   Umatilla        59   59249   65136
## 4   31726985    Wallowa        63    6911    7538
## 5   20302669     Morrow        49    7625    9408
## 6   20357027      Union        61   23598   25160
## 7   12416975    Gilliam        21    1717    1981
## 8   11156812  Tillamook        57   21570   24405
## 9     722111 Washington        67  311554  391928
## 10   8220778    Sherman        55    1918    1809
## 11   4814434  Multnomah        51  583887  629165
## 12   5301756 Hood River        27   16903   19674
## 13  23998934      Wasco        65   21683   23225
## 14  18820812  Clackamas         5  278850  329251
## 15   7128672    Yamhill        71   65551   79861
## 16  11863489     Marion        47  228483  264680
## 17  30732579      Baker         1   15317   16527
## 18   7476106       Polk        53   49541   59546
## 19  17141015    Wheeler        69    1396    1691
## 20  10063651    Lincoln        41   38889   45766
## 21  45067081      Grant        23    7853    7985
## 22  18002958  Jefferson        31   13676   16677
## 23  23204363       Linn        43   91227  103605
## 24    682247     Benton         3   70811   76605
## 25  29883668      Crook        13   14111   16891
## 26  98839546    Malheur        45   26038   28671
## 27  30510858  Deschutes        17   74958  101221
## 28  46274196       Lane        39  282912  309646
## 29 102459641     Harney        25    7060    7066
## 30  50730226    Douglas        19   94649  101921
## 31  61226755    Klamath        35   57702   63137
## 32  83569499       Lake        37    7186    7315
## 33  16264708       Coos        11   60273   63235
## 34  28034331    Jackson        29  146389  171236
## 35  16587075      Curry        15   19327   21230
## 36  16439447  Josephine        33   62649   73305
relocate(base, CNTY_FIPS:POP1997)
##    CNTY_FIPS      AREA POP1990 POP1997       NAME
## 1          7   8165371   33301   35287    Clatsop
## 2          9   6998979   37557   43645   Columbia
## 3         59  32882502   59249   65136   Umatilla
## 4         63  31726985    6911    7538    Wallowa
## 5         49  20302669    7625    9408     Morrow
## 6         61  20357027   23598   25160      Union
## 7         21  12416975    1717    1981    Gilliam
## 8         57  11156812   21570   24405  Tillamook
## 9         67    722111  311554  391928 Washington
## 10        55   8220778    1918    1809    Sherman
## 11        51   4814434  583887  629165  Multnomah
## 12        27   5301756   16903   19674 Hood River
## 13        65  23998934   21683   23225      Wasco
## 14         5  18820812  278850  329251  Clackamas
## 15        71   7128672   65551   79861    Yamhill
## 16        47  11863489  228483  264680     Marion
## 17         1  30732579   15317   16527      Baker
## 18        53   7476106   49541   59546       Polk
## 19        69  17141015    1396    1691    Wheeler
## 20        41  10063651   38889   45766    Lincoln
## 21        23  45067081    7853    7985      Grant
## 22        31  18002958   13676   16677  Jefferson
## 23        43  23204363   91227  103605       Linn
## 24         3    682247   70811   76605     Benton
## 25        13  29883668   14111   16891      Crook
## 26        45  98839546   26038   28671    Malheur
## 27        17  30510858   74958  101221  Deschutes
## 28        39  46274196  282912  309646       Lane
## 29        25 102459641    7060    7066     Harney
## 30        19  50730226   94649  101921    Douglas
## 31        35  61226755   57702   63137    Klamath
## 32        37  83569499    7186    7315       Lake
## 33        11  16264708   60273   63235       Coos
## 34        29  28034331  146389  171236    Jackson
## 35        15  16587075   19327   21230      Curry
## 36        33  16439447   62649   73305  Josephine
relocate(base, AREA, .after=POP1997)
##          NAME CNTY_FIPS POP1990 POP1997      AREA
## 1     Clatsop         7   33301   35287   8165371
## 2    Columbia         9   37557   43645   6998979
## 3    Umatilla        59   59249   65136  32882502
## 4     Wallowa        63    6911    7538  31726985
## 5      Morrow        49    7625    9408  20302669
## 6       Union        61   23598   25160  20357027
## 7     Gilliam        21    1717    1981  12416975
## 8   Tillamook        57   21570   24405  11156812
## 9  Washington        67  311554  391928    722111
## 10    Sherman        55    1918    1809   8220778
## 11  Multnomah        51  583887  629165   4814434
## 12 Hood River        27   16903   19674   5301756
## 13      Wasco        65   21683   23225  23998934
## 14  Clackamas         5  278850  329251  18820812
## 15    Yamhill        71   65551   79861   7128672
## 16     Marion        47  228483  264680  11863489
## 17      Baker         1   15317   16527  30732579
## 18       Polk        53   49541   59546   7476106
## 19    Wheeler        69    1396    1691  17141015
## 20    Lincoln        41   38889   45766  10063651
## 21      Grant        23    7853    7985  45067081
## 22  Jefferson        31   13676   16677  18002958
## 23       Linn        43   91227  103605  23204363
## 24     Benton         3   70811   76605    682247
## 25      Crook        13   14111   16891  29883668
## 26    Malheur        45   26038   28671  98839546
## 27  Deschutes        17   74958  101221  30510858
## 28       Lane        39  282912  309646  46274196
## 29     Harney        25    7060    7066 102459641
## 30    Douglas        19   94649  101921  50730226
## 31    Klamath        35   57702   63137  61226755
## 32       Lake        37    7186    7315  83569499
## 33       Coos        11   60273   63235  16264708
## 34    Jackson        29  146389  171236  28034331
## 35      Curry        15   19327   21230  16587075
## 36  Josephine        33   62649   73305  16439447
relocate(base, AREA, .before=CNTY_FIPS)
##          NAME      AREA CNTY_FIPS POP1990 POP1997
## 1     Clatsop   8165371         7   33301   35287
## 2    Columbia   6998979         9   37557   43645
## 3    Umatilla  32882502        59   59249   65136
## 4     Wallowa  31726985        63    6911    7538
## 5      Morrow  20302669        49    7625    9408
## 6       Union  20357027        61   23598   25160
## 7     Gilliam  12416975        21    1717    1981
## 8   Tillamook  11156812        57   21570   24405
## 9  Washington    722111        67  311554  391928
## 10    Sherman   8220778        55    1918    1809
## 11  Multnomah   4814434        51  583887  629165
## 12 Hood River   5301756        27   16903   19674
## 13      Wasco  23998934        65   21683   23225
## 14  Clackamas  18820812         5  278850  329251
## 15    Yamhill   7128672        71   65551   79861
## 16     Marion  11863489        47  228483  264680
## 17      Baker  30732579         1   15317   16527
## 18       Polk   7476106        53   49541   59546
## 19    Wheeler  17141015        69    1396    1691
## 20    Lincoln  10063651        41   38889   45766
## 21      Grant  45067081        23    7853    7985
## 22  Jefferson  18002958        31   13676   16677
## 23       Linn  23204363        43   91227  103605
## 24     Benton    682247         3   70811   76605
## 25      Crook  29883668        13   14111   16891
## 26    Malheur  98839546        45   26038   28671
## 27  Deschutes  30510858        17   74958  101221
## 28       Lane  46274196        39  282912  309646
## 29     Harney 102459641        25    7060    7066
## 30    Douglas  50730226        19   94649  101921
## 31    Klamath  61226755        35   57702   63137
## 32       Lake  83569499        37    7186    7315
## 33       Coos  16264708        11   60273   63235
## 34    Jackson  28034331        29  146389  171236
## 35      Curry  16587075        15   19327   21230
## 36  Josephine  16439447        33   62649   73305

FILTRAR CASOS (filter())

Esta función filtrar los casos (las filas) según una condición.

Por ejemplo, seleccionar todas las unidades administrativas con una superfie superior a >= 4000000 millas cuadradas.

filter(base, AREA >= 30000000)
##         NAME CNTY_FIPS      AREA POP1990 POP1997
## 1   Umatilla        59  32882502   59249   65136
## 2    Wallowa        63  31726985    6911    7538
## 3      Baker         1  30732579   15317   16527
## 4      Grant        23  45067081    7853    7985
## 5    Malheur        45  98839546   26038   28671
## 6  Deschutes        17  30510858   74958  101221
## 7       Lane        39  46274196  282912  309646
## 8     Harney        25 102459641    7060    7066
## 9    Douglas        19  50730226   94649  101921
## 10   Klamath        35  61226755   57702   63137
## 11      Lake        37  83569499    7186    7315

Se pueden incluir varias condiciones en un mismo filtro. Así por ejemplo, el siguiente ejemplo filtra aquellas filas con un CNTY_FIPS >= 55 y un AREA < 80000000.

filter(base, CNTY_FIPS >= 55 & AREA < 80000000)
##         NAME CNTY_FIPS     AREA POP1990 POP1997
## 1   Umatilla        59 32882502   59249   65136
## 2    Wallowa        63 31726985    6911    7538
## 3      Union        61 20357027   23598   25160
## 4  Tillamook        57 11156812   21570   24405
## 5 Washington        67   722111  311554  391928
## 6    Sherman        55  8220778    1918    1809
## 7      Wasco        65 23998934   21683   23225
## 8    Yamhill        71  7128672   65551   79861
## 9    Wheeler        69 17141015    1396    1691

En este caso, el operador %in% (pertenece al conjunto) significa que el programa busca dentro de la variable esos caracteres.

filter(base, AREA >= 30000000, NAME %in% c("Lane", "Grant"))
##    NAME CNTY_FIPS     AREA POP1990 POP1997
## 1 Grant        23 45067081    7853    7985
## 2  Lane        39 46274196  282912  309646

También se utilizan operadores como | (o)

filter(base, CNTY_FIPS >= 60 | CNTY_FIPS <= 15)
##          NAME CNTY_FIPS     AREA POP1990 POP1997
## 1     Clatsop         7  8165371   33301   35287
## 2    Columbia         9  6998979   37557   43645
## 3     Wallowa        63 31726985    6911    7538
## 4       Union        61 20357027   23598   25160
## 5  Washington        67   722111  311554  391928
## 6       Wasco        65 23998934   21683   23225
## 7   Clackamas         5 18820812  278850  329251
## 8     Yamhill        71  7128672   65551   79861
## 9       Baker         1 30732579   15317   16527
## 10    Wheeler        69 17141015    1396    1691
## 11     Benton         3   682247   70811   76605
## 12      Crook        13 29883668   14111   16891
## 13       Coos        11 16264708   60273   63235
## 14      Curry        15 16587075   19327   21230

Se pueden eliminar las filas por su posición

filter(base, !row_number() %in% c(1, 12:31))
##          NAME CNTY_FIPS     AREA POP1990 POP1997
## 1    Columbia         9  6998979   37557   43645
## 2    Umatilla        59 32882502   59249   65136
## 3     Wallowa        63 31726985    6911    7538
## 4      Morrow        49 20302669    7625    9408
## 5       Union        61 20357027   23598   25160
## 6     Gilliam        21 12416975    1717    1981
## 7   Tillamook        57 11156812   21570   24405
## 8  Washington        67   722111  311554  391928
## 9     Sherman        55  8220778    1918    1809
## 10  Multnomah        51  4814434  583887  629165
## 11       Lake        37 83569499    7186    7315
## 12       Coos        11 16264708   60273   63235
## 13    Jackson        29 28034331  146389  171236
## 14      Curry        15 16587075   19327   21230
## 15  Josephine        33 16439447   62649   73305

Como se ha señalado anteriormente, es conveniente verificar siempre la existencia de valores ausentes (codificados como ‘NA’). En las siguientes líneas, se muestra cómo tratarlos. Primero, se creará un dataframe artificial:

df <- data.frame(team=c('A', 'A', 'B', 'B', 'C', 'C'),
                 points=c(4, NA, 7, 5, 9, 9),
                 assists=c(1, 3, 5, NA, 2, 2))
df
##   team points assists
## 1    A      4       1
## 2    A     NA       3
## 3    B      7       5
## 4    B      5      NA
## 5    C      9       2
## 6    C      9       2

Para eliminar cualquier fila que contenga NAs.

na.omit(df)
##   team points assists
## 1    A      4       1
## 3    B      7       5
## 5    C      9       2
## 6    C      9       2

Si lo que se quiere eliminar son los NA de una columna específica

filter(df, !is.na(points))
##   team points assists
## 1    A      4       1
## 2    B      7       5
## 3    B      5      NA
## 4    C      9       2
## 5    C      9       2

También es conveniente verificar si hay valores duplicados, y eliminarlos en caso de observar que constituyen un error.

distinct(df)
##   team points assists
## 1    A      4       1
## 2    A     NA       3
## 3    B      7       5
## 4    B      5      NA
## 5    C      9       2

ORDENAR CASOS (arrange())

La función arrange() se utiliza para ordenar las filas de un data frame de acuerdo a una o varias columnas/variables. Por defecto arrange() ordena las filas por orden ascendente:

Echemos un vistazo al data frame arrange: nombre_dataframe

Para ordenar las filas por la variable wind de forma ascendente podemos hacer lo siguiente: arrange(nombre_dataframe, wind)

Si las queremos ordenar de forma ascendente lo haremos del siguiente modo: arrange(nombre_dataframe, desc(wind))

Podemos ordenar las filas según varias variables: arrange(nombre_dataframe, wind, date)

create data frame df <- data.frame(player = c(‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’), points = c(12, 14, 14, 15, 20, 18, 29), assists = c(3, 5, 7, 8, 14, NA, 9))

view data frame df

Ejemplo 1: Organizar por una columna Ordenar en orden ascendente en función de los valores de la columna ‘puntos’:

df %>% arrange(points)

Para ordenar en orden descendente, podemos usar la función desc():

df %>% arrange(desc(points))

Note that NA’s will be sorted to the end, whether or not you sort ascending or decsending:

df %>% arrange(assists)

df %>% arrange(desc(assists))

Para organizar las filas por varias columnas, simplemente podemos proporcionar más nombres de columna como argumentos: ordenar por puntos, luego asistencias df %>% arrange(points, assists)

También podemos ordenar las filas por una columna ascendente y otra descendente: ordenar por puntos ascendentes, luego asistencias descendentes df %>% arrange(points, desc(assists))

Organizar filas en un orden personalizado: ordenar por jugador con orden personalizado df %>% arrange(factor(player, levels = c(‘D’, ‘C’, ‘A’, ‘B’, ‘E’, ‘F’, ‘G’)))

SLICE

Puede usar la función slice() del paquete dplyr en R para subconjuntos de filas en función de sus ubicaciones de enteros. Puede utilizar los siguientes métodos para crear subconjuntos de determinadas filas en un marco de datos: En los ejemplos siguientes se muestra cómo usar cada método con el siguiente marco de datos:

create conjunto de datos df <- data.frame(team=c(‘A’, ‘A’, ‘A’, ‘B’, ‘B’, ‘C’, ‘C’), points=c(1, 2, 3, 4, 5, 6, 7), assists=c(1, 5, 2, 3, 2, 2, 0))

#view dataset df

Subconjunto de una fila específica (obtener solo la fila 3) df %>% slice(3) Subconjunto de varias filas (obtener las filas 2, 5 y 6) df %>% slice(2, 5, 6) Subconjunto A Rango de filas (obtener filas 1 a 3) df %>% slice(1:3) Subconjunto de filas por grupo (obtener la primera fila por grupo) df %>% group_by(team) %>% slice(1)

TRABAJANDO CON GRUPOS

GROUP BY La función group_by() agrupa un conjunto de filas seleccionado en un conjunto de filas de resumen de acuerdo con los valores de una o más columnas o expresiones.

Echemos un vistazo al data frame pollution: pollution

Agrupemos las observaciones por la variable city: group_by(pollution, city)

df %>% group_by(team) %>% filter(any(points == 10))

Esta sintaxis particular agrupa un marco de datos por la columna denominada equipo y filtra solo los grupos en los que al menos un valor de la columna de puntos es igual a 10.

En el ejemplo siguiente se muestra cómo utilizar esta sintaxis en la práctica.

Ejemplo: Agrupar por y filtrar datos usando dplyr Supongamos que tenemos el siguiente marco de datos en R que contiene información sobre varios jugadores de baloncesto:

create data frame df <- data.frame(team=c(‘A’, ‘A’, ‘A’, ‘B’, ‘B’, ‘B’, ‘C’, ‘C’, ‘C’), points=c(10, 15, 8, 4, 10, 10, 12, 12, 7))

view data frame df

Podemos usar el siguiente código para agrupar el marco de datos por el valor en la columna del equipo y, a continuación, filtrar todos los grupos que no tengan al menos un valor en la columna de puntos igual a 10:

group por equipo y filtrar los equipos en los que ningún valor de puntos es igual a 10 df %>% group_by(team) %>% filter(any(points == 10))

Observe que todas las filas en las que el equipo es igual a “C” se filtran porque no hay ningún valor en la columna de puntos para el equipo “C” igual a 10.

Tenga en cuenta que este es solo un ejemplo de un filtro que podríamos aplicar.

Por ejemplo, podríamos aplicar otro filtro en el que filtremos por equipos en los que al menos un valor de la columna de puntos sea mayor que 13:

group por equipo y filtrar los equipos en los que ningún valor de puntos es superior a 13 df %>% group_by(team) %>% filter(any(points > 13))

Tenga en cuenta que solo se mantienen las filas en las que el equipo es igual a “A”, ya que este es el único equipo con al menos un valor de puntos superior a 13.

Summarise

La función group_by() es extremadamente útil trabajando en conjunción con la función summarise(): pollution %>% group_by(city) %>% summarise(mean = mean(amount), sum = sum(amount), n = n())

La función summarise() funciona de forma análoga a la función mutate, excepto que en lugar de añadir nuevas columnas crea un nuevo data frame.

Para obtener un resumen con la mediana y la varianza de la variable amount podemos hacer lo siguiente: summarise(pollution, mediana = median(amount), variance = var(amount))

Podemos utilizar el operador %>%, pollution %>% summarise(mediana = median(amount), variance = var(amount))

A continuación se muestran funciones que trabajando conjuntamente con la función summarise() facilitarán nuestro trabajo diario. Las primeras pertenecen al paquete base y las otras son del paquete dplyr. Todas ellas toman como argumento un vector y devuelven un único resultado. base min(), max() Valores max y min mean() media median() mediana sum() suma de los valores var, sd() varianza y desviación típica dplyr first() primer valor en un vector last() el último valor en un vector n() el número de valores en un vector n_distinct() el número de valores distintos en un vector nth() Extrar el valor que ocupa la posición n en un vector

obtain rows and columns of mtcars dim(mtcars)

view first six rows of mtcars head(mtcars)

               mpg cyl disp  hp drat    wt  qsec vs am gear carb

Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

La sintaxis básica que usaremos para agrupar y resumir datos es la siguiente:

data %>% group_by(col_name) %>% summarize(summary_name = summary_function) Nota: Las funciones summarize() y summarise() son equivalentes.

Ejemplo 1: Encuentre la media y la mediana por grupo. El siguiente código muestra cómo calcular las medidas de tendencia central por grupo, incluidas la media y la mediana:

find mpg promedio por cilindro mtcars %>% group_by(cyl) %>% summarize(mean_mpg = mean(mpg, na.rm = TRUE))

find mpg medio por cilindro mtcars %>% group_by(cyl) %>% summarize(median_mpg = median(mpg, na.rm = TRUE))

Ejemplo 2: Buscar medidas de propagación por grupo El siguiente código muestra cómo calcular las medidas de dispersión por grupo, incluida la desviación estándar, el rango intercuartílico y la desviación absoluta mediana:

find sd, IQR y loco por cilindro

mtcars %>% group_by(cyl) %>% summarize(sd_mpg = sd(mpg, na.rm = TRUE), iqr_mpg = IQR(mpg, na.rm = TRUE), mad_mpg = mad(mpg, na.rm = TRUE))

Ejemplo 3: Buscar recuento por grupo. En el código siguiente se muestra cómo encontrar el recuento y el recuento único por grupo en R:

find recuento de filas y recuento de filas único por cilindro mtcars %>% group_by(cyl) %>% summarize(count_mpg = n(), u_count_mpg = n_distinct(mpg))

Ejemplo 4: Buscar percentil por grupo. El siguiente código muestra cómo encontrar el percentil 90 de los valores de mpg por grupo de cilindros:

find 90th percentile of mpg for each cylinder group mtcars %>% group_by(cyl) %>% summarize(quant90 = quantile(mpg, probs = .9))

UNGROUP()

Puede usar la función ungroup() en dplyr para desagrupar filas después de usar la función group_by() para resumir una variable por grupo.

create marco de datos df <- data.frame(team=c(‘A’, ‘A’, ‘A’, ‘B’, ‘B’, ‘B’), points=c(14, 18, 22, 26, 36, 34), assists=c(5, 4, 4, 8, 7, 3)) view marco de datos df

Ahora supongamos que usamos el siguiente código para calcular el valor medio de los puntos, agrupados por equipo:

calculate media de puntos, agrupados por equipo df_new <- df %>% group_by(team) %>% summarize(mean_points = mean(points)) %>% ungroup() resultados df_new

Con esta sintaxis, podemos calcular el valor medio de los puntos agrupados por equipo, pero hemos perdido la columna de asistencias.

Para conservar la columna de asistencias, podemos usar mutate() en lugar de summarize() y aún usar ungroup() al final:

Calcular la media de puntos, agrupados por equipo df_new <- df %>% group_by(team) %>% mutate(mean_points = mean(points)) %>% ungroup()

Ver resultados df_new

Esta vez podemos mantener la columna de asistencias y usando la función mutate() simplemente hemos agregado una nueva columna llamada mean_points que muestra el valor medio de puntos para cada equipo. Dado que también usamos la función ungroup(), podemos realizar cálculos en este nuevo marco de datos sin preocuparnos de que ningún cálculo se vea afectado por ninguna variable agrupada. Si no usáramos la función ungroup(), las filas del marco de datos aún estarían agrupadas, lo que podría tener consecuencias no deseadas cuando realicemos más cálculos más adelante.

How to Do a Left Join in R (With Examples) You can use the merge() function to perform a left join in base R: merge(df1,df2, all.x=TRUE) You can also use the left_join() function from the dplyr package to perform a left join: dplyr::left_join(df2, df1) Note: If you’re working with extremely large datasets, the left_join() function will tend to be faster than the merge() function.

define first data frame df1 <- data.frame(team=c(‘Mavs’, ‘Hawks’, ‘Spurs’, ‘Nets’), points=c(99, 93, 96, 104)) df1

define second data frame df2 <- data.frame(team=c(‘Mavs’, ‘Hawks’, ‘Spurs’, ‘Nets’), rebounds=c(25, 32, 38, 30), assists=c(19, 18, 22, 25)) df2

Example 1: Left Join Using Base R We can use the merge() function in base R to perform a left join, using the ‘team’ column as the column to join on: perform left join using base R merge(df1, df2, by=‘team’, all.x=TRUE)

Example 2: Left Join Using dplyr We can use the left_join() function from the dplyr package to perform a left join, using the ‘team’ column as the column to join on: perform left join using dplyr left_join(df1, df2, by=‘team’)

One difference you’ll notice between these two functions is that the merge() function automatically sorts the rows alphabetically based on the column you used to perform the join. Conversely, the left_join() function preserves the original order of the rows from the first data frame.

Example: Perform Left Join Using Selected Columns in dplyr Suppose we have the following two data frames in R:

create first data frame df_A <- data.frame(team=c(‘A’, ‘B’, ‘C’, ‘D’, ‘E’), points=c(22, 25, 19, 14, 38))

df_A

create second data frame df_B <- data.frame(team=c(‘A’, ‘C’, ‘D’, ‘F’, ‘G’), conference=c(‘W’, ‘W’, ‘E’, ‘E’, ‘E’), rebounds=c(14, 8, 8, 6, 9), assists=c(4, 3, 9, 9, 4))

df_B

We can use the following syntax in dplyr to perform a left join but only bring in columns team and conference from df_B:

perform left join but only bring in team and conference columns from df_B final_df <- df_A %>% left_join(select(df_B, team, conference), by=“team”)

view final data frame final_df

The resulting data frame contains all rows from df_A and only the rows in df_B where the team values matched. By using the select() function from dplyr, we were able to specify that we only wanted to bring in the team and conference columns from df_B. Notice that the rebounds and assists columns from df_B were not included in the final data frame.

EL OPERADOR pipe %>%

En conjunto, estas propiedades hacen que sea fácil encadenar múltiples pasos simples para lograr un resultado complejo. Para concatenar o unir estas funciones se utilizan las tuberías o pipes %>% que están disponibles a través del paquete magrittr instalado como parte de dplyr. Las tuberías ayudan a hacer que R sea expresivo, como un lenguaje hablado.

Lee las tuberías como “entonces” o “luego”. El siguiente ejemplo entonces puede leerse como: “tomo el conjunto de datos mtcars y filtra aquellos coches con 4 cilindros (cyl)”.

El operador pipeline %>% es útil para concatenar múltiples dplyr operaciones. Obsérvese en el siguiente ejemplo, que cada vez que queremos aplicar mas de una función, la instrucción es una secuencia de llamadas a funciones de forma anidada y que resulta ilegible: third(second(first(x))) Este anidamiento no es una forma natural de expresar un secuencia de operaciones. El operador %>% nos permite escribir una secuencia de operaciones de izquierda a derecha: first(x) %>% second(x) %>% third(x)

Obsérvese que las siguientes instrucciones,

select(nombre_dataframe, storm, pressure) filter(nombre_dataframe, wind >= 50)

es lo mismo que,

nombre_dataframe %>% select(storm, pressure) nombre_dataframe %>% filter(wind >= 50)

Es en el siguiente ejemplo donde podemos observar el verdadero potencial del operador pipeline:

nombre_dataframe %>% filter(wind>=50) %>% select(storm, pressure)

ACTIVIDADES DE EVALUACIÓN CONTINUA

Ejercicio 1.

  1. La superficie de cada condado está recogida en millas cuadradas. Transforme esta superficie a km^2 (multiplicando por 2,58999).

  2. Calcule la densidad de la población en habitantes por km^2 (cociente entre la población de una localidad y su superficie total).

  3. Calcule el índice masculinidad de cada condado como cociente entre la población masculina y la población femenina multiplicado por 100.

  4. Calcule el % que representa la población divida en grandes grupos de edad.

  5. Calcule el Índice de envejecimiento como cociente entre la población mayor de 64 años y la población de menos de 20 años.

  6. Calcule el Índice de dependencia global como cociente entre la población potencialmente dependiente (edad inferior a 20 años) y la población en edad potencialmente activa (desde los 20 hasta los 64 años).

  7. Calcule el Índice de nupcialidad que corresponde al porcentaje de solteros respecto a la población que ha contraído matrimonio en algún momento, independientemente de su estado civil actual.

  8. Calcule el % que representa la población según grandes grupos étnicos.

Ejercicio 2

Gama de criminales

range(Boston[,c(“crim”)])

¿Cuántos de los suburbios de este conjunto de datos limitan con el río Charles?

Boston %>% select(chas) %>% filter(chas == 1) %>% # (1 if tract bounds river; 0 otherwise) count(chas) #35 Suburbs are bound to the Charles River

¿Cuál es la mediana de la proporción de alumnos por alumno entre las ciudades de este conjunto de datos? ¿Cuál es la significa? median(Boston$ptratio) #The median is 19.05 among the towns

En este conjunto de datos, ¿cuántos de los suburbios tienen un promedio de más de siete habitaciones por vivienda? ¿Más de ocho habitaciones por vivienda? Comenta sobre los suburbios que tienen un promedio de más de ocho habitaciones por vivienda.

Boston %>% select(rm) %>% filter(rm > 7) %>% count(rm) #62 suburbs has more than seven rooms per dwelling.

¿Más de ocho habitaciones por vivienda?

Boston %>% select(rm) %>% filter(rm > 8) %>% count(rm) #13 suburbs has more than seven rooms per dwelling.

Convierte chas en un factor. Boxplot el medv contra chas. ¿Son más caras las casas alrededor del río Charles?

boxplot(Boston$medv~y, data=ToothGrowth, notch=FALSE, main=“Price for Houses around the Charles River”, xlab=“0 - Not Charles River | 1 - Charles River”, ylab=“Median value of owner-occupied homes in $1000’s”)

Ejercicio 3.

  1. Resume múltiples columnas

¿Cuál es la media del largo y ancho de sépalos y pétalos para cada especie?

iris %>% group_by(Species) %>% summarise_all(mean)

Si deseas resumir solo ciertas columnas, utiliza las funciones summarise_at o summarise_if.

  1. Selecciona filas con los valores máximos en cada grupo

Imagina que solo quieres obtener aquel caso con mayor valor de largo de sépalos, para cada especie.

iris %>% + group_by(Species) %>% + filter(Sepal.Length == max(Sepal.Length)) %>% + arrange(Species)

De manera equivalente, puedes utilizar:

iris %>% + group_by(Species) %>% + slice(which.max(Sepal.Length))

  1. Aplica una función a cada fila de una tabla

Y si quieres calcular una nueva variable (columna) con el valor máximo del largo de sépalos o pétalos, hacemos lo siguiente:

iris %>% + rowwise() %>% + mutate(Max.Len= max(Sepal.Length,Petal.Length))

  1. Obtén las frecuencias relativas o proporciones.¿Qué % de casos tenemos por especie?

iris %>% + group_by(Species) %>% + summarise(n = n()) %>% + mutate(freq = n / sum(n))

En este caso tenemos 50 flores (casos o filas) para cada especie, por lo tanto hay un 33% de la muestra que corresponde a la especie de lirio setosa, 33% a veriscolor y 33% a virginica.

  1. Elimina filas duplicadas

Esta es una tarea habitual en la manipulación de datos, pero es fácil de solucionar con la función distinct():

iris %>% distinct(Species)

Nos muestra que la variable “Species” solo tiene 3 etiquetas distintas: setosa, virginica y vericolor

  1. Cambia el nombre de las variables

A menudo necesitamos acortar los nombres de las variables (columnas), para escribir código de manera más sencilla y rápida. Por ejemplo, aquí llamaremos “SL” a la variable “Sepal.Length” para evitar errores de escritura con las mayúsculas, puntos y el inglés. Para renombrar las columnas podemos escribir lo siguiente:

iris %>% rename(SL = Sepal.Length) %>% head()

Vemos que solo ha cambiado el nombre de la columna seleccionada.

  1. Selecciona casos completos

¿Cómo quedarnos con las observaciones completas? Construimos un conjunto de datos ficticio que contenga valores perdidos (o faltantes, NA) para el ejemplo:

df <- tibble(x = c(1, 2, NA), y = c(“a”, NA, “b”)) df

Si queremos descartar todos los casos que contengan NA, escribimos:

df %>% drop_na()

Si queremos descartar casos en función de una variable:

df %>% drop_na(x)

También puedes utilizar las funciones na.omit(), filter(complete.cases(.)) o filter(!is.na(x1)).

  1. Suma a través de múltiples filas/columnas

Creamos un conjunto de datos de ejemplo, imagina que 1 es presencia y 0 ausencia (o Sí/NO).

df=data.frame( x1=c(1,0,0,NA,0,1,1,NA,0,1), x2=c(1,1,NA,1,1,0,NA,NA,0,1), x3=c(0,1,0,1,1,0,NA,NA,0,1)) df

iris %>% group_by(Species) %>% count()

  1. Cuenta el número de casos o filas

Esto es muy importante, porque cuando resumimos los datos en estadísticos descriptivos deberíamos tener en cuenta el número de observaciones que se ha utilizado para ello:

iris %>% group_by(Species) %>% summarise(n = n(), meanSL = mean(Sepal.Length))

También puedes utilizar la función count:

iris %>% group_by(Species) %>% count()

  1. Cuenta el número de casos o filas únicas (con NAs)

Cuando tenemos un conjunto de datos con NAs podemos contar el número de casos o filas únicas con la función n_distinct().

Creamos un conjunto de datos ficticio para el ejemplo.

data=data.frame(aa=c(1,2,3,4,NA), bb=c(‘a’, ‘b’, ‘a’, ‘c’, ‘c’)) data

data %>%
filter(!is.na(aa)) %>%
group_by(bb) %>%
summarise(Unique_Elements = n_distinct(aa))

Por ejemplo, aquí hemos seleccionado los casos que no tienen NA en la variable “aa” y contado los casos distintos para la variable “bb”.

Ejercicio 4

El conjunto de datos starwars viene incorporado en el paquete dplyr y corresponde a un conjunto de datos (un tibble) con 87 filas y 14 variables:

  • name: nombre del personaje

  • height: altura (cm)

  • mass: peso (kg)

  • hair_color,skin_color,eye_color: color de cabello, piel y ojos

  • birth_year: año de nacimiento (ABY = Antes de la Batalla de Yavin)

  • sex: sexo biológico del personaje, es decir, masculino, femenino, hermafrodita o ninguno (como en el caso de los droides).

  • gender: rol de género o la identidad de género del personaje según lo determinado por su personalidad o la forma en que fueron programados (como en el caso de los droides).

  • homeworld: nombre del mundo natal

  • species: nombre de la especie

  • films: lista de películas en las que apareció el personaje

  • vehicles: lista de vehículos que ha pilotado el personaje

  • starships: lista de naves estelares que el personaje ha pilotado

¿Qué especies son androides? > starwars %>% filter(species == “Droid”)

¿de qué color son los personales? (Vamos a seleccionar aquellas variables que indican el color del personaje). > starwars %>% select(name, ends_with(“color”))

¿Qué personajes están en forma? Para responder a esta pregunta primero podríamos calcular el índice de masa corporal (BMI) con la fórmula bmi = mass / ((height / 100) ^ 2) y quedarnos tan solo con los casos cuyo bmi se encuentre entre 18.5-24.9. > starwars %>% mutate(name, bmi = mass / ((height / 100) ^ 2)) %>% select(name:mass, bmi) %>% filter(between(bmi, 18.5, 24.9))

¿Cuál es el peso medio por especie, y qué especies con más de 50kg (y más de 1 caso o sujeto). > starwars %>% group_by(species) %>% summarise(n = n(), mass = mean(mass, na.rm = TRUE)) %>% filter(n > 1, mass > 50)