4 Transformar filas
4.1 Introducción
En este apartado estudiaremos las dos funciones más importantes del paquete dplyr que operan sobre las filas del marco de datos: filter()
y arrange()
. Todas tienen como misión principal modificar el número o el orden de las filas de un marco de datos dejando el número o el orden de las columnas inalterado. En resumen, diremos que son dos funciones complementarias:
-
filter()
: modifica las filas que están presentes sin cambiar su orden. -
arrange()
: cambia el orden de las filas sin reducir las filas que están presentes.
4.2 Filter
La función filter()
reduce el número de filas de un marco de datos siguiendo el criterio determinado por una operación lógica. Su funcionalidad es muy parecida a lo que ya hemos aprendido en el capítulo 2, donde después de un marco de datos introducíamos en los corchetes una operación lógica para reducir el número de filas de un marco de datos. En este caso, en la función filter()
introducimos el nombre del marco de datos como primer argumento y la operación lógica como segundo argumento.
filter(df, operación_lógica)
En el sistema pipe, conseguiremos exactamente el mismo propósito de la siguiente forma:
df |>
filter(operación_lógica)
Veamos algunos ejemplos de cómo utilizar la función filter()
en el marco de datos nuts_mini
. A continuación, usamos los principales operadores relacionales (==
, !=
, >
, <
, etc.) que conocemos para filtrar el marco de datos según un determinado criterio. Como se puede ver, la función mantiene solo las filas que coinciden con el criterio en cuestión, y el número de columnas se mantiene inalterado.
Es importante tener en cuenta que hay dos tipos de iguales, que conviene distinguir.
- El igual doble (
==
) lo utilizamos para establecer condiciones lógicas. Tenemos que pensar que el único propósito que tiene es crearnos, implícita o explícitamente, un vector lógico. - El igual sencillo (
=
) nos hace asignaciones de valores o resultados. Lo usamos principalmente en cualquier función para asignar parámetros a determinados argumentos.
Solo queremos conservar las filas que correspondan a Bélgica.
nuts_mini |>
filter(country == "Belgium")
# A tibble: 3 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Belgium Région De Bruxelles-Capitale 68400 4.37 50.8 4.37 4.35
2 Belgium Région Wallonne 28100 5.01 50.3 4.69 4.62
3 Belgium Vlaams Gewest 39800 4.24 51.0 5.66 5.22
Solo queremos conservar las observaciones que sean Laponia o Galicia.
Solo queremos conservar las observaciones que tengan el PIB per cápita superior a 65.000 euros por habitante.
nuts_mini |>
filter(gdpcap_nuts > 65000)
# A tibble: 6 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Belgium Région De Bruxelles-Capita… 68400 4.37 50.8 4.37 4.35
2 Denmark Hovedstaden 66200 12.9 55.7 NA NA
3 Ireland Southern 73700 -8.44 52.3 5.59 5.21
4 Ireland Eastern And Midland 66000 -7.00 53.3 5.50 5.53
5 Luxembourg Luxembourg 97400 6.09 49.8 NA NA
6 Sweden Stockholm 65300 18.2 59.5 6.41 5.57
Solo queremos conservar las regiones NUTS que estén situadas a una latitud inferior o igual a 30 grados.
nuts_mini |>
filter(lat <= 30)
# A tibble: 6 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Spain Canarias 20400 -15.7 28.3 4.82 5.09
2 France Guadeloupe 22100 -61.6 16.3 NA NA
3 France Martinique 23600 -61.0 14.7 NA NA
4 France Guyane 15100 -53.2 3.92 NA NA
5 France La Réunion 21600 55.5 -21.1 NA NA
6 France Mayotte 9500 45.1 -12.8 NA NA
Aparte de usar los operadores relacionales básicos, también podemos ubicar dentro de filter()
todas aquellas funciones que produzcan como resultado un vector lógico. Veamos algunas de las más habituales.
La función between()
nos genera una condición lógica entre dos valores numéricos. Primero introduciremos el nombre del vector, a continuación el número pequeño y después el número grande. Todo lo que esté entre estos dos valores (ambos incluidos) será TRUE
y todo lo que quede fuera será FALSE
.
A continuación, hemos filtrado las observaciones que están situadas cerca del meridiano de Greenwich (es decir, de una longitud de –1 a una longitud de +1).
nuts_mini |>
filter(between(lon, -1, 1))
# A tibble: 10 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Spain Comunidad Valenciana 21900 -0.557 39.4 4.87 4.38
2 Spain Aragón 27100 -0.665 41.5 5.67 4.9
3 France Pays De La Loire 30200 -0.823 47.5 4.59 5.02
4 France Aquitaine 29900 -0.236 44.3 4.27 4.45
5 France Poitou-Charentes 27400 -0.0818 46.2 4.27 5.44
6 France Basse-Normandie 26500 -0.517 48.9 4.6 5.57
7 United Kingdom East Midlands (Engl… 25100 -0.808 52.9 5.27 5.09
8 United Kingdom East Of England 28500 0.537 52.2 5.33 5.14
9 United Kingdom London 56200 -0.111 51.5 5.48 4.54
10 United Kingdom South East (England) 33900 -0.531 51.3 5.43 5.18
La función str_detect()
, del paquete stringr, es muy útil para encontrar observaciones que correspondan a un determinado valor categórico. Por ejemplo, supongamos que queremos buscar la observación que corresponde a Cataluña pero no sabemos si el nombre estará en catalán (Catalunya), castellano (Cataluña) o inglés (Catalonia). Podemos filtrar las observaciones que incluyan "Catal"
para encontrarla.
nuts_mini |>
filter(str_detect(name, "Catal"))
# A tibble: 1 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Spain Cataluña 29700 1.53 41.8 5.18 4.01
También podemos crear vectores lógicos que nos marquen TRUE
en el caso que la observación sea igual al máximo o el mínimo de un determinado vector numérico. En el código siguiente nos hemos preguntado cuál es la región NUTS del marco de datos con más PIB per cápita. En las variables que contienen NAs, tendremos que usar el argumento na.rm = T
para que nos devuelva un resultado.
La función is.na()
genera una condición lógica que devuelve TRUE
si algún valor del vector está perdido (NA
) y FALSE
si no lo está. Esta función es muy útil para observar los NA de una variable, pero también para eliminarlos. Si los queremos eliminar, solo habrá que escribir el símbolo !
. ¿En qué observaciones no tenemos información sobre el PIB per cápita? En el código siguiente lo averiguamos.
# A tibble: 16 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Estonia Kirde-Eesti NA 27.3 59.2 5.33 4.90
2 Estonia Kesk-Eesti NA 25.6 59.1 5.57 5.37
3 Croatia Grad Zagreb NA 16.0 45.8 NA NA
4 Croatia Zagrebačka Županija NA 16.1 45.8 NA NA
5 Croatia Krapinsko-Zagorska Županija NA 15.9 46.1 NA NA
6 Croatia Varaždinska Županija NA 16.3 46.2 NA NA
7 Croatia Koprivničko-Križevačka Župan… NA 16.8 46.1 NA NA
8 Croatia Međimurska Županija NA 16.5 46.4 NA NA
9 Croatia Bjelovarsko-Bilogorska Župan… NA 17.0 45.7 NA NA
10 Croatia Virovitičko-Podravska Župani… NA 17.6 45.7 NA NA
11 Croatia Požeško-Slavonska Županija NA 17.6 45.4 NA NA
12 Croatia Brodsko-Posavska Županija NA 17.8 45.2 NA NA
13 Croatia Osječko-Baranjska Županija NA 18.5 45.6 NA NA
14 Croatia Vukovarsko-Srijemska Županija NA 18.9 45.2 NA NA
15 Croatia Karlovačka Županija NA 15.5 45.3 NA NA
16 Croatia Sisačko-Moslavačka Županija NA 16.4 45.4 NA NA
Dentro de la función filter()
también podemos combinar varios vectores mediante los operadores booleanos. Para recordar su funcionamiento, volvemos a incluir el vídeo que lo explica en el margen derecho de la pantalla. Recordamos que:
- El operador AND (
&
) devuelve TRUE solo si se cumplen todas las condiciones. - El operador OR (
|
) devuelve TRUE si se cumple una de las condiciones. - El operador NOT (
!
) devuelve lo contrario de la condición.
La condición AND se puede hacer tanto con el símbolo &
como separando las dos operaciones con una coma. En el ejemplo siguiente buscamos qué regiones NUTS tienen un PIB per cápita inferior a 10.000 euros y están posicionadas de media por encima del 6 en el eje izquierda-derecha. (Conseguiremos el mismo resultado si en lugar de una coma escribimos el símbolo &
.)
nuts_mini |>
filter(gdpcap_nuts < 10000, lrscale > 6)
# A tibble: 4 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Lithuania Marijampolės Apskritis 9100 23.2 54.7 5.74 6.68
2 Poland Podkarpackie 8500 22.2 50.0 3.46 7.12
3 Poland Świętokrzyskie 8700 20.8 50.8 3.07 6.79
4 Poland Lubelskie 8400 22.9 51.2 4.28 6.05
¿Sabes que las regiones NUTS situadas más a la izquierda y a la derecha del mapa son francesas? En el código siguiente utilizamos el operador OR para filtrar por los datos que cumplan cualquiera de los dos requisitos: que coincida o bien con el mínimo de longitud o bien con el máximo de longitud.
¿Y cuáles son las regiones situadas más en el extremo de Europa? En este caso hemos introducido cuatro condiciones lógicas incorporando también el mínimo y el máximo de latitud. Si hemos filtrado los datos por cuatro condiciones, ¿por qué nos devuelve tres observaciones y no cuatro?
# A tibble: 3 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Finland Lappi 38800 26.3 67.7 6.82 5.51
2 France Guadeloupe 22100 -61.6 16.3 NA NA
3 France La Réunion 21600 55.5 -21.1 NA NA
El motivo es que la isla de la Reunión es a la vez la región con el mínimo de latitud y el máximo de longitud.
¿Qué observaciones pertenecen a Austria pero no a la capital Viena? En este filtro se utiliza el operador AND porque queremos que cumpla dos condiciones, pero negamos la segunda condición del filtro con el símbolo !
. Esto hará que nos mantenga todas las observaciones menos Wien
.
nuts_mini |>
filter(country == "Austria" & !name == "Wien")
# A tibble: 8 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Austria Burgenland 29800 16.5 47.5 5.86 4.97
2 Austria Niederösterreich 34900 15.8 48.3 5.74 5.28
3 Austria Kärnten 35500 13.9 46.8 4.92 5.09
4 Austria Steiermark 38400 15.0 47.3 4.7 4.94
5 Austria Oberösterreich 42900 14.0 48.1 6.08 5.59
6 Austria Salzburg 50200 13.1 47.4 5.57 4.75
7 Austria Tirol 44600 11.5 47.2 5.06 5.03
8 Austria Vorarlberg 46000 9.89 47.2 4.77 4.97
También conseguiríamos el mismo resultado escribiendo name != "Wien"
en el segundo argumento.
Finalmente, otra opción que permite filtrar datos es encadenar varios filtros con el sistema pipe.
- La primera pipe nos hace una primera criba de los datos.
- A partir del resultado del primer filtro, la segunda pipe nos hace una segunda selección.
Los factores están organizados internamente por niveles. Supongamos, por ejemplo, que tenemos una variable con 180 países almacenada en forma de factor. Si aplicamos un filtraje en el que solo conservamos 12 países en el marco de datos, dplyr mantendrá las etiquetas de los niveles. Para eliminar los niveles vacíos, deberemos crear otra pipe después de la función filter()
y añadir la función droplevels()
.
Veamos dos ejemplos en los que la pipe nos sería de utilidad:
Si quitamos Francia, ¿cuáles son las regiones que se encuentran situadas más al extremo de Europa? En el primer filtro se excluyen las observaciones de Francia, de forma que en el segundo filtro solo se tendrá en cuenta la longitud de las observaciones que no estén en territorio francés.
¿Cuál es la región de España con la ideología más de derechas? En el primer filtro reducimos el marco de datos a solo las observaciones NUTS de España. El segundo filtro buscará el valor máximo solo de este subconjunto. Encontramos que son las Islas Baleares.
Hay que remarcar, finalmente, que podemos añadir tantas condiciones como queramos siempre que no filtremos más de la cuenta, puesto que en este caso R nos devolverá un marco de datos sin ninguna observación.
4.3 Arrange
La función arrange()
simplemente ordena las filas de forma ascendente o descendente a partir de los valores de una columna determinada. Probablemente es, pues, la función que tiene menos misterio de todos los verbos que estudiaremos del paquete dplyr.
- Ordenaremos el marco de datos de forma ascendente por el vector que indicamos dentro de la función
arrange()
. - Si queremos que el orden sea descendente, especificaremos
desc()
dentro de la función. - Los vectores de carácter los ordena por orden alfabético. Los números, por orden ascendente. En los factores, el orden dependerá de si hemos otorgado una ordinalidad implícita.
El código siguiente ordena el marco de datos por el vector lrscale
en orden ascendente, de forma que primero aparecerán las observaciones con un valor más bajo.
nuts_mini |>
arrange(lrscale)
# A tibble: 368 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Germany Brandenburg 28300 13.4 52.5 4.71 3.81
2 Spain País Vasco 32100 -2.62 43.0 5.72 3.86
3 Germany Saarland 35500 6.95 49.4 5.22 4
4 Spain Cataluña 29700 1.53 41.8 5.18 4.01
5 Germany Mecklenburg-Vorpommern 27400 12.5 53.8 4.80 4.08
6 Germany Berlin 39300 13.4 52.5 5.54 4.10
7 Spain Cantabria 22800 -4.03 43.2 6.12 4.11
8 Germany Bremen 47600 8.74 53.2 5.75 4.12
9 Germany Sachsen-Anhalt 27300 11.7 52.0 5 4.14
10 Finland Åland 47500 20.1 60.2 5.93 4.15
# ℹ 358 more rows
El código siguiente ordena el marco de datos por el vector lrscale
en orden descendente, de forma que primero aparecerán las observaciones con un valor más alto.
nuts_mini |>
arrange(desc(lrscale))
# A tibble: 368 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Poland Podkarpackie 8500 22.2 50.0 3.46 7.12
2 Hungary Nyugat-Dunántúl 13600 17.0 47.2 4.66 6.99
3 Poland Świętokrzyskie 8700 20.8 50.8 3.07 6.79
4 Lithuania Marijampolės Apskritis 9100 23.2 54.7 5.74 6.68
5 Italy Friuli-Venezia Giulia 30800 13.1 46.2 3 6.61
6 Finland Päijät-Häme 32600 25.7 61.2 6.5 6.32
7 Finland Keski-Pohjanmaa 37100 24.1 63.6 6.64 6.31
8 Poland Pomorskie 11700 18.0 54.2 5.05 6.23
9 Hungary Pest 10300 19.4 47.4 3.76 6.22
10 Finland Etelä-Pohjanmaa 32400 23.0 62.7 6.6 6.12
# ℹ 358 more rows
arrange()
permite combinar varios criterios de ordenación. Solo hay que separar los criterios por comas. Primero ordenará los datos siguiendo el primer criterio, y, en caso de haber algún empate, el criterio utilizado para desempatar será el que indicamos en segundo lugar. Por ejemplo, en el código siguiente primero ordenamos los datos por país en orden ascendente (alfabéticamente) y después en escala izquierda-derecha y orden descendente.
nuts_mini |>
arrange(country, desc(lrscale))
# A tibble: 368 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Austria Oberösterreich 42900 14.0 48.1 6.08 5.59
2 Austria Niederösterreich 34900 15.8 48.3 5.74 5.28
3 Austria Kärnten 35500 13.9 46.8 4.92 5.09
4 Austria Tirol 44600 11.5 47.2 5.06 5.03
5 Austria Vorarlberg 46000 9.89 47.2 4.77 4.97
6 Austria Burgenland 29800 16.5 47.5 5.86 4.97
7 Austria Steiermark 38400 15.0 47.3 4.7 4.94
8 Austria Salzburg 50200 13.1 47.4 5.57 4.75
9 Austria Wien 49500 16.4 48.2 5.10 4.36
10 Belgium Vlaams Gewest 39800 4.24 51.0 5.66 5.22
# ℹ 358 more rows
4.4 Combinación con pipe
Ahora que ya conocemos dos funciones de dplyr, filter()
y arrange()
, podemos combinarlas mediante el sistema pipe para responder algunas preguntas sobre los datos.
En España, ¿cuáles son las regiones más ricas? En primer lugar, tendremos que filtrar los datos por España y, en segundo lugar, ordenar por el vector gdpcap_nuts
en orden descendente.
nuts_mini |>
filter(country == "Spain") |>
arrange(desc(gdpcap_nuts))
# A tibble: 19 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Spain Comunidad De Madrid 34200 -3.72 40.5 5.01 4.61
2 Spain País Vasco 32100 -2.62 43.0 5.72 3.86
3 Spain Comunidad Foral De Navarra 30500 -1.65 42.7 6 4.37
4 Spain Cataluña 29700 1.53 41.8 5.18 4.01
5 Spain Illes Balears 27100 2.91 39.6 4.29 5.33
6 Spain Aragón 27100 -0.665 41.5 5.67 4.9
7 Spain La Rioja 26600 -2.52 42.3 4.8 5.12
8 Spain Castilla Y León 23100 -4.78 41.8 4.91 5.15
9 Spain Cantabria 22800 -4.03 43.2 6.12 4.11
10 Spain Galicia 22300 -7.91 42.8 4.93 4.35
11 Spain Comunidad Valenciana 21900 -0.557 39.4 4.87 4.38
12 Spain Principado De Asturias 21900 -5.99 43.3 4.56 4.62
13 Spain Región De Murcia 20700 -1.49 38.0 4.83 4.88
14 Spain Canarias 20400 -15.7 28.3 4.82 5.09
15 Spain Castilla-La Mancha 19600 -3.01 39.6 4.87 4.86
16 Spain Ciudad Autónoma De Ceuta 19500 -5.34 35.9 2.57 4.17
17 Spain Andalucía 18500 -4.58 37.5 4.64 4.29
18 Spain Extremadura 18200 -6.15 39.2 5.61 4.46
19 Spain Ciudad Autónoma De Melilla 17900 -2.95 35.3 NA NA
En el Reino Unido, ¿cuáles son las regiones que tienen una población con menos grado de confianza en las otras personas? En primer lugar, tendremos que filtrar los datos por el Reino Unido y, en segundo lugar, ordenar por el vector ppltrst
en orden ascendente.
nuts_mini |>
filter(country == "United Kingdom") |>
arrange(ppltrst)
# A tibble: 12 × 7
country name gdpcap_nuts lon lat ppltrst lrscale
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 United Kingdom West Midlands (Engla… 26100 -2.27 52.5 4.95 4.92
2 United Kingdom Northern Ireland 24400 -6.69 54.6 5 5.07
3 United Kingdom Yorkshire And The Hu… 27500 -1.23 54.0 5.02 4.80
4 United Kingdom North West (England) 30700 -2.72 54.1 5.09 4.91
5 United Kingdom Wales 22900 -3.77 52.3 5.14 4.71
6 United Kingdom East Midlands (Engla… 25100 -0.808 52.9 5.27 5.09
7 United Kingdom East Of England 28500 0.537 52.2 5.33 5.14
8 United Kingdom South East (England) 33900 -0.531 51.3 5.43 5.18
9 United Kingdom London 56200 -0.111 51.5 5.48 4.54
10 United Kingdom North East (England) 25900 -1.90 55.0 5.52 5.01
11 United Kingdom South West (England) 27000 -3.15 51.0 5.74 5.04
12 United Kingdom Scotland 29300 -4.18 56.8 5.9 4.74
4.5 Otras funciones
Tanto filter()
como arrange()
modifican las filas del marco de datos sin alterar o reducir el número de columnas. A continuación, veremos otras funciones que también operan sobre las filas, pero, en cambio, la transformación que realizamos normalmente también acaba modificando las columnas.
La función count()
hace el recuento del número de categorías de un vector. Este vector es habitualmente un vector de carácter o un factor, pero también podría ser uno de tipo numérico con un número limitado de categorías. Como vemos, Grecia es el país que tiene más observaciones, seguido de Rumanía y Bulgaria. Como cada observación es una subdivisión territorial, significa que Grecia tiene 52 regiones registradas en este marco de datos.
nuts_mini |>
count(country, sort = T)
# A tibble: 28 × 2
country n
<chr> <int>
1 Greece 52
2 Romania 42
3 Bulgaria 28
4 France 27
5 Croatia 21
6 Italy 21
7 Finland 19
8 Spain 19
9 Poland 17
10 Germany 16
# ℹ 18 more rows
La función distinct()
conserva las filas con valores únicos y opcionalmente modifica las columnas. No es una función que utilizaremos muy a menudo.
nuts_mini |>
distinct(country)
# A tibble: 28 × 1
country
<chr>
1 Austria
2 Belgium
3 Bulgaria
4 Cyprus
5 Czech Republic
6 Germany
7 Denmark
8 Estonia
9 Greece
10 Spain
# ℹ 18 more rows