💡 MATERIALES PARA LA ACTIVIDAD:
► Script.
Para no tener que cargar los paquetes dplyr y
tidyr, se procede a activar el paquete
tidyverse ya que incluye los dos anteriores como
dependencias
library(tidyverse)
Establecer la carpeta de trabajo
setwd("D:/G2040/TEMA_1_Introduccion/TEMA_1_ejercicios")
Cargar el fichero
load("zonas_verdes.RData")
filter())Esta función filtrar los casos (las filas) según una o más condiciones.
Un primer caso son aquellas filas que incluyen datos perdidos (NA). Si queremos eliminar todas las filas que poseen algún dato perdido…
borrame <- zonas_verdes %>%
na.omit()
… o sólo aquellas que contienen NA en variables concretas.
borrame <- zonas_verdes %>%
filter(!is.na(obras))
También es posible eliminar filas con valores duplicados
borrame %>%
distinct()
Por último, podríamos eliminar algunas filas que ocupan posiciones determinadas, por ejemplo, las filas 10, 20, y 40.
borrame %>%
filter(!row_number() %in% c(10, 20, 40))
Igualmente, podemos eliminar filas que cumplen una codición
específica, por ejemplo, aquellas filas que en el dataframe
borrame pertenecen al barrio de Retiro y su
superficie es mayor que 5 ha.
borrame <- zonas_verdes %>%
select(barrio, superficie) %>%
filter(barrio == "Retiro" & superficie > 6)
Un tipo particular de uso tiene que ver con la selección de fechas. Primero se debe comprobar que la variable en cuestión es de tipo Date.
str(zonas_verdes)
Si la variable no fuera de tipo Date, habría que convertirla usando la siguiente sintaxis: df <- df %>% mutate(fecha = as.Date(fecha)). Si además, el formato no fuera yyyy-mm-dd, podría usarse el siguiente: df <- df %>% mutate(fecha = as.Date(fecha, format = “%d/%m/%Y”)). Para filtrar un única fecha:
zonas_verdes %>%
filter(fecha_inauguracion == as.Date("1999-08-28"))
Si se quiere un rango de fechas, hay dos posiblidades:
zonas_verdes %>%
filter(fecha_inauguracion >= as.Date("1972-01-01"),
fecha_inauguracion <= as.Date("2023-12-31"))
o también
zonas_verdes %>%
filter(fecha_inauguracion >= as.Date("1972-01-01") & fecha_inauguracion <= as.Date("2023-03-31"))
Si sólo nos interesa filtar uno o varios unidades de
tiempo, es necesario cargar el paquete
lubridate.
library(lubridate)
zonas_verdes %>%
filter(year(fecha_inauguracion) == 1944) # Filtrar por año
zonas_verdes %>%
filter(month(fecha_inauguracion) == 3) # Filtrar por mes específico (ej. marzo)
zonas_verdes %>%
filter(day(fecha_inauguracion) == 15) # Filtrar por día del mes
Como resumen:
| Acción | Código base |
|---|---|
| Igualdad | filter(fecha == as.Date(“2023-01-01”)) |
| Rango de fechas | filter(fecha >= … & fecha <= …) |
| Por año / mes / día | filter(year(fecha) == 2023) (con lubridate) |
| Día de la semana | filter(wday(fecha) == 2) o == “Mon” |
distinct()La función distinct() puede usarse para filtrar
aquellos valores (únicos) que se repiten en una variable, por
ejemplo, los de la variable parques
zonas_verdes %>%
distinct(parques)
Téngase en cuenta que sólo se devuelven los valores únicos de la columna solicitada. También es posible filtrar los valores equivalentes de varias variables simultáneamente.
zonas_verdes %>%
distinct(parques, obras)
Filtar los valores únicos de todas las columnas.
zonas_verdes %>%
distinct(obras)
En el caso de trabajar con cadenas de caracteres, se
pueden filtrar siempre que añadamos la función grepl() a
continuación de la función filter(), por ejemplo, filtrar
filas de barrio que contienen la cadena Arg
(de Arganzuela).
zonas_verdes %>%
filter(grepl("Arg", barrio))
Aquí se filtran las filas que que contienen una u otra cadena (|).
zonas_verdes %>%
filter(grepl("Ret|Sal", barrio))
También podemos pedir lo contrario, seleccionar aquellos caso que no contienen cierta combinación de caracteres.
zonas_verdes %>%
filter(!grepl("Ret", barrio))
Las condiciones pueden ser expresiones logicas construidas mediante los operadores relacionales y lógicos:
| Símbolo | Significado |
|---|---|
| < | Menor que |
| > | Mayor que |
| == | Igual que |
| <= | Menor o igual que |
| >= | Mayor o igual que |
| != | Diferente que |
| %in% | Pertenece al conjunto |
| is.na | Es NA |
| !is.na | No es NA |
| Simbolo | Significado |
|---|---|
| & | boolean and |
| | boolean o | |
| xor | or inclusivo |
| ! | not |
| any | cualquiera true |
| all | todos verdaderos |
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:
zonas_verdes %>%
arrange(superficie)
Para ordenar de manera descendente, se anida la
función desc().
zonas_verdes %>%
arrange(desc(superficie))
Los casos ausentes (NA’s) aparecen ordenados al final, tanto con orden ascente como descendente:
Para organizar las filas según varias columnas, simplemente se proporcionan más nombres de columna como argumentos:
zonas_verdes %>%
arrange(barrio, superficie)
Es posible combinar una columna en orden ascendente y otra en orden descendente:
zonas_verdes %>%
arrange(barrio, desc(superficie))
También es factible ordenar las filas de acuerdo con orden preestablecido. Para ello, es necesario incluir un factor con unos niveles ordeandos de manera específica.
zonas_verdes %>%
arrange(factor(obras, levels = c("NA", "NO", "SI")))
Hay otras opciones para ordenar el dataframe. Por ejemplo,
ordenar las filas de acuerdo con un orden prefijado de la
variable barrio y, a continuación, utilizar los valores de
la variable superficie (orden ascendente). Esto se
consigue usando la función match()dentro de la función
arrange(). La función match() obtiene el
índice de fila de los valores de la columna barrio y, a
continuación, la función arrange() ordenar en función de
estos valores de índice. Para ordenar en función de los valores de
puntos descendentes, basta con utilizar desc() en su
lugar.
zonas_verdes %>%
arrange(match(barrio, c("Arganzuela", "Retiro")), superficie)
También es posible ordenar los casos según grupos de
variables. Por ejemplo, es posible ordenar los casos según la
variable superficie según un orden ascendente, pero
agrupados según la variable barrio
zonas_verdes %>%
group_by(barrio) %>%
arrange(superficie, .by_group=TRUE)
En el caso de utilizar un orden descendente
zonas_verdes %>%
group_by(barrio) %>%
arrange(desc(superficie), .by_group=TRUE)
También es posible organizar las filas según múltiples grupos:
zonas_verdes %>%
group_by(barrio, obras) %>%
arrange(superficie, .by_group=TRUE)
También es posible ordenar variables como cadenas.
zonas_verdes %>%
arrange(desc(barrio))
Hay dos posiblidades;
bind_rows().Es la función clásica de dplyr/tidyverse para unir filas de dos o más
data frames. La sintaxis es
bind_rows(df1, df2, df3, .id = "source").
Peculiaridades:
df1 <- data.frame(a = 1:2, b = 3:4)
df2 <- data.frame(a = 5:6, c = 7:8)
bind_rows(df1, df2)
Otra posible opción es añadir nuevas filas a un dataframe
preexistente mediante la función add_row(), que agrega
filas a un dataframe existente, una fila a la vez o de manera
programática. Presenta la siguiente sintaxis básica:
add_row(.data, .before=NULL, .after=NULL)
donde:
Tenga en cuenta que si no especifica ningún valor para los argumentos .before o .after, la nueva fila simplemente se añadirá al final del data frame.
A continuación se crea un nuevo dataframe denominado
zonas_verdes_actualizado.
zonas_verdes_actualizado <- data.frame(ID=c(101, 102, 103, 104),
barrio=c("Monte Carmelo", "Las Tablas", "Valdebebas", "Valdebebas"),
parques=c(2, 8, 1, 5),
superficie=c(3.021, 2.807, 2.456, 3.632),
obras = c(0, 1, NA, 0),
fecha_inauguracion = c("2024-12-05", "2005-09-20", "2012-07-01", "2000-03-06"))
zonas_verdes_actualizado
Hemos olvidado un nuevo caso en el dataframe
zonas_verdes_actualizado, por lo que lo creamos y lo unimos
al anterior dataframe.
zonas_verdes_actualizado %>%
add_row(ID=105, barrio = "Las Tablas", parques = 3, superficie = 0.87, obras = 1, fecha_inauguracion = "2025-10-01")
Puede ser interesante unir a este dataframe un nuevo caso, por ejemplo al inicio…
zonas_verdes_actualizado %>%
add_row(ID=100, barrio = "Sanchinarro", parques = 6, superficie = 3.7, obras = NA, fecha_inauguracion = "2023-01-10", .before=1)
… o al final
zonas_verdes_actualizado %>%
add_row(ID=107, barrio = "Sanchinarro", parques = 6, superficie = 3.7, obras = NA, fecha_inauguracion = "2023-01-10", .after=3)
Resumen de diferencias
| Característica | bind_rows() | add_row() |
|---|---|---|
| Tipo de operación | Combina data frames | Agrega filas individuales a un dataframe |
| Cantidad de filas | Varias filas de varios dataframe | Una o pocas filas a la vez |
| Columnas nuevas | Sí, rellena con NA si no existen | Deben existir en el tibble |
| Uso típico | Unión de datasets | Añadir manualmente datos |
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.
Podemos usar el siguiente código para agrupar el dataframe por el
valor en la columna barrio a continuación, filtrar todos
casos que tengan más de 18 elementos (en este caso, filas)
zonas_verdes %>%
group_by(barrio) %>%
filter(n() > 18)
Filtrar el parque más grande de cada barrio
zonas_verdes %>%
group_by(barrio) %>%
filter(superficie == max(superficie))
Filtrar los barrios que no tienen obras en los parques.
zonas_verdes %>%
group_by(barrio) %>%
filter(any(obras == 0))
Filtrar parques cuya superficie está por encima del promedio del barrio
zonas_verdes %>%
group_by(barrio) %>%
filter(superficie > mean(superficie, na.rm = TRUE))
Filtrar parques inaugurados antes del promedio de año del barrio
library(lubridate)
zonas_verdes %>%
mutate(año = year(fecha_inauguracion)) %>%
group_by(barrio) %>%
filter(año < 1952)
Filtrar el parque más pequeño de cada barrio con obras
zonas_verdes %>%
filter(obras == 1) %>%
group_by(barrio) %>%
filter(superficie == min(superficie))
Seleccionar los tres parques más grandes en cada barrio que tengan obras.
zonas_verdes %>%
filter(obras == 1) %>%
group_by(barrio) %>%
slice_max(superficie, n = 3)
zonas_verdes %>%
group_by(barrio) %>%
filter(any(superficie >= 10))
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.
Summarise()La función summarize() (o su sinónimo summarise()) se
usa para resumir un conjunto de datos, generalmente después de agruparlo
con group_by(). 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.
Por ejemplo, para el cálculo del promedio de superficie de los parques por barrio…
… el total de parques por barrio
zonas_verdes %>%
group_by(barrio) %>%
summarise(total_parques = sum(parques))
… el número de observaciones (filas) por barrio
zonas_verdes %>%
group_by(barrio) %>%
summarise(n = n())
… el promedio y desviación estándar de la superficie por barrio
zonas_verdes %>%
group_by(barrio) %>%
summarise(
media_superficie = mean(superficie, na.rm = TRUE),
sd_superficie = sd(superficie, na.rm = TRUE))
… o calcular la superficie total y promedio, ordenando resultados
zonas_verdes %>%
group_by(barrio) %>%
summarise(
total_superficie = sum(superficie, na.rm = TRUE),
promedio_superficie = mean(superficie, na.rm = TRUE)) %>%
arrange(desc(total_superficie))
… o calcular para cada barrio, cuántos parques tiene (n()), su superficie media, el año más reciente de inauguración, y el porcentaje de parques con obras (mean(obras == 1)).
zonas_verdes %>%
group_by(barrio) %>%
summarise(
n_parques = n(),
superficie_media = mean(superficie, na.rm = TRUE),
anio_reciente = max(lubridate::year(fecha_inauguracion), na.rm = TRUE),
pct_con_obras = mean(obras == 1) * 100)
También se pueden conocer algunos estadísticos, como la mediana y la varianza de la variable superficie:
## mediana variance
## 1 7.327894 7.084933
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.
| Función | Resultado |
|---|---|
| min(), max() Valores max y min | |
| mean() | media |
| median() | mediana |
| sum() | suma de los valores |
| var, sd() | varianza y desviación típica |
| 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() | Extrer el valor que ocupa la posición n en un vector |
Puede usar la función slice() para subconjuntos
de filas en función de sus ubicaciones. Ofrece diversas
opciones, como seleccionar un subconjunto de una fila específica (la
fila 3)…
zonas_verdes %>%
slice(3)
… un subconjunto de varias filas (filas 2, 5 y 6)
zonas_verdes %>%
slice(2, 5, 6)
… un subconjunto de un rango de filas (filas 1 a 3)
zonas_verdes %>%
slice(1:3)
… un subconjunto de filas por grupo (mostrar la primera fila de barrios)
zonas_verdes %>%
group_by(barrio) %>%
slice(1)
slice_max()La función slice_max() usa la siguiente sintaxis:
slice_max(.data, order_by, n, …)
en la que:
Por ejemplo, nos interesa conocer el valor más alto
de la variable superficie.
zonas_verdes %>%
slice_max(superficie)
Si no especifica un valor para el argumento n, la función
slice_max() devolverá por defecto la(s) fila(s) con el
valor más grande en una variable concreta. Si se especifica un valor de
n, devuelva las n filas con
el valor más alto de esa variable.
zonas_verdes %>%
slice_max(superficie, n=5)
En caso de empates, se devuelven ambas filas.
zonas_verdes %>%
slice_max(parques, n = 4)
Su función contraria es slice_min(), cuya sintaxis es
exactamente igual a la de slice_max(). Por ejemplo, nos
interesa conocer el valor más bajo de la variable
parques.
zonas_verdes %>%
slice_min(parques)
Si se especifica un valor de n, devuelva las
n filas con el valor más bajo de esa variable.
zonas_verdes %>%
slice_min(parques, n=5)
sample_n()Para seleccionar una muestra aleatoria de filas por grupo se utiliza
la función sample_n() junto con la función
group_by(). La función sample_n() utiliza la
siguiente sintaxis básica:
sample_n(tbl, size, replace=FALSE, …)
donde:
Supongamos que queremos seleccionar aleatoriamente 3
elementos de cada barrio, si bien podemos especificar un valor
diferente para el argumento size para devolver un número
diferente de elementos
zonas_verdes %>%
group_by(barrio) %>%
sample_n(size=3)
Tenga en cuenta que cada vez que ejecutamos este código, las filas
seleccionadas para cada grupo pueden ser diferentes, ya que la función
sample_n() selecciona las filas al azar. Si desea que el
código sea reproducible, puede utilizar la función
set.seed() para establecer una «semilla» aleatoria que nos
permita seleccionar las mismas filas aleatorias cada vez.
set.seed(1)
zonas_verdes %>%
group_by(barrio) %>%
sample_n(size=2)
📝 ACTIVIDAD EN EL AULA :
Se volverá a trabajar con el fichero denominado inmobiliaria. Lo importaremos desde el servidor de la UC, si no disponemos de él.
url <- "https://personales.unican.es/rasillad/docencia/g2040/tema_1/datos/inmobiliaria.csv"
inmobiliaria <- read.csv(url, sep = ";", dec = ",", header = TRUE)
head(inmobiliaria)
## barrio precio superficie accesibilidad habitaciones banos terraza garaje
## 1 San Agustin 173800 43.2 2 3 1 2 2
## 2 San Agustin 204600 56.9 1 2 1 2 2
## 3 San Agustin 220000 66.4 2 2 2 1 1
## 4 San Agustin 270600 61.9 1 3 2 2 2
## 5 San Agustin 297000 89.8 3 3 2 2 1
## 6 San Agustin 308000 71.0 2 3 2 1 2
## comunidad valor disponible
## 1 110.0 32830.6 1
## 2 151.8 59400.0 2
## 3 0.0 34624.5 1
## 4 286.0 59400.0 1
## 5 0.0 87047.4 2
## 6 264.0 68520.1 2
Cambiamos el nombre de la variable banos por baños.
inmobiliaria <- inmobiliaria %>%
rename(baños = banos)
inmobiliaria %>%
filter(barrio == "San Agustin") %>%
arrange(precio)
inmobiliaria %>%
distinct(barrio) %>%
arrange(barrio)
inmobiliaria %>%
filter(superficie > 60) %>%
sample_n(3)
inmobiliaria %>%
slice(1:5) %>%
arrange(desc(valor))
inmobiliaria %>%
filter(disponible == 1) %>%
distinct(barrio)
inmobiliaria %>%
add_row(barrio = "Centro", precio = 250000, superficie = 70,
accesibilidad = 2, habitaciones = 2, baños = 1,
terraza = 1, garaje = 1, comunidad = 100, valor = 50000, disponible = 1) %>%
arrange(habitaciones)
nuevos <- data.frame(
barrio = "Nuevo Barrio", precio = 180000, superficie = 60,
accesibilidad = 1, habitaciones = 2, baños = 1,
terraza = 1, garaje = 1, comunidad = 90, valor = 40000, disponible = 1
)
inmobiliaria %>%
bind_rows(nuevos) %>%
arrange(precio)
inmobiliaria %>%
filter(barrio == "El Robledo") %>%
arrange(desc(precio)) %>%
slice(1:3)
inmobiliaria %>%
add_row(barrio = "San Pedro", precio = 220000, superficie = 65,
accesibilidad = 2, habitaciones = 3, baños = 2,
terraza = 1, garaje = 1, comunidad = 120, valor = 60000, disponible = 2) %>%
distinct(barrio)
inmobiliaria %>%
filter(baños >= 2) %>%
sample_n(5) %>%
arrange(desc(precio))