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.
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.
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.
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.
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)
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())
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)
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
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
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
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’)))
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)
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))
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.
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)
La superficie de cada condado está recogida en millas cuadradas. Transforme esta superficie a km^2 (multiplicando por 2,58999).
Calcule la densidad de la población en habitantes por km^2 (cociente entre la población de una localidad y su superficie total).
Calcule el índice masculinidad de cada condado como cociente entre la población masculina y la población femenina multiplicado por 100.
Calcule el % que representa la población divida en grandes grupos de edad.
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.
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).
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.
Calcule el % que representa la población según grandes grupos étnicos.
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”)
¿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.
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))
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))
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.
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
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.
¿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)).
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()
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()
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”.
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)