Capítulo 2 Cartografía con R

R13 es un proyecto de software libre, se basa en el lenguaje S, desarrollado originalmente en Bell Laboratories en los años 70 y 80 del siglo pasado. R como tal fue desarrollado en la University of Auckland, New Zealand. Comenzó como proyecto en 1992 y la primera versión estable se lanzó en 2000.

Funciona sobre Linux, Unix, Mac OSX, Windows y Android. Como principales características se puede remarcar la facilitad que presenta para trabajar con estructuras de datos y con gráficos, y el repositorio de paquetes disponibles para su uso.

En lugar de trabajar directamente sobre R, usaremos RStudio14, entorno de desarrollo integrado (IDE) especialmente pensado para R que permite ejecución paso a paso, visualización del entorno, acceso a la ayuda, formateo del código, definición de proyectos y directorio de trabajo, entre otras funcionalidades.

Objetivos del capítulo

  • Repasar conceptos sobre Información Geográfica explicados en clase de Teoría, materializados en un SIG concreto.
  • Introducir R mediante ejemplos y utilizarlo como SIG. No es una introducción exhaustiva sino un primer contacto; se pondrá en práctica:
    • Estructuras de datos.
    • Control de flujo.
    • Funciones.
    • Plots básicos.
    • Paquetes.
    • Visualización de capas.
    • Crear un mapa temático.

Las actividades propuestas se basan principalmente en Lovelace, Nowosad, and Muenchow (2019), Brundsdon and Comber (2019) y Matloff (2011).

2.1 Obtención de los datos

Usaremos los mismos datos que hemos usado en QGIS. En el apartado 1.1, se describe cómo obtenerlos.

En QGIS, cuando se añaden nuevos campos, se modifican las tablas de atributos de las capas en los archivos de estas. Por este motivo, aunque se hayan obtenido para QGIS, es recomendable usar otra carpeta de trabajo y obtener de nuevo las capas. En el caso del archivo Excel descargado del INE, podemos generar de nuevo la tabla de datos o copiar el archivo de la tabla y eliminar la columna adicional añadida en QGIS.

Ejercicio 2.1 Descarga de https://doi.org/10.6084/m9.figshare.11295644.v1 el archivo Granada.zip que contiene los archivos de las capas que se usarán como fuente de datos. Extrae todos archivos y sitúalos en una carpeta de trabajo. No es necesario documentar la realización de este apartado.

Obtén los datos del censo de los municipios de Granada para los años posteriores a 2018 en formato de tabla. No es necesario documentar la realización de este ejercicio.

2.2 Introducción a R mediante ejemplos

A continuación, se van a introducir distintos aspectos de R mediante ejemplos. No pretende ser un resumen de R sino un repaso de aspectos que se van a utilizar al tratar información geográfica. Se pueden encontrar verdaderas introducciones a R, por ejemplo, en:

  • W. J. Owen: The R Guide15.
  • G. Rodríguez: Introducing R16.
  • P. Torfs, C. Braue: A (very) short introduction to R17.
  • W. N. Venables, D. M. Smith and the R Core Team: An Introduction to R18.

The R Guide de Owen es posiblemente la más conocida.

2.2.1 Entorno de trabajo

Crear un nuevo script.

Figura 2.1: Crear un nuevo script.

Trabajaremos con R desde RStudio: iniciamos RStudio que a su vez inicia R. La manera más inmediata de realizar un desarrollo sencillo en R es creando un nuevo archivo de R (un script), como se muestra en la figura 2.1, pulsando sobre el icono New file y seleccionando la opción R Script.

Ejecutar un nuevo script.

Figura 2.2: Ejecutar un nuevo script.

La ventana de RStudio tiene cuatro zonas (figura 2.2). La parte superior izquierda corresponde al archivo R que acabamos de crear. En esta zona iremos escribiendo el código R que desarrollemos (en este caso, copiando-y-pegando el de las figuras siguientes). Para ejecutar una línea de código, situamos el cursor en cualquier parte de la línea y pulsamos sobre el botón Run (figura 2.2). El cursor se sitúa en la línea siguiente para que podamos ejecutarla al volver a pulsar sobre el mismo botón19. En la ventana inferior izquierda, Console, se muestran las instrucciones que se envían a R y el resultado que este devuelve. Si se ha creado o modificado una variable, el resultado se puede ver en la ventana de la parte superior derecha, Environment.

Ejercicio 2.2 Ejecuta los fragmentos de código siguientes y trata de entender cómo se producen los resultados que se obtienen. No es necesario documentar la realización de este ejercicio.

2.2.2 Funcionamiento general

2.2.2.1 Asignación y operaciones aritméticas

A continuación, se muestran dos formas de asignación (= y <-). En la mayoría de la situaciones resultan equivalentes; puede haber diferencias dependiendo del ámbito de las variables. En las guías de estilo de R se recomienda usar una de ellas pero siempre la misma. Al poner una asignación entre paréntesis adicionalmente se presenta el resultado obtenido.

x <- 3
y <- 2
x + y
z <- x + y
z
(z <- x + y)

x = 3
y = 2
x + y
z = x + y
z
(z = x + y)

2.2.2.2 Constantes y funciones

En R se pueden usar funciones y constantes predefinidas. Obtenemos ayuda sobre lo que hace una función ejecutando la línea con ? y el nombre de la función. La ayuda aparecerá en la zona inferior derecha de la ventana, se activará automáticamente la pestaña Help.

2 * pi * z
sqrt(z)

?sqrt

Pulsando sobre el botón Show in new window de la zona de ayuda de la ventana, se abre la ayuda en una nueva ventana, para poder leerla más fácilmente.

2.2.2.3 Definición y operaciones matemáticas con vectores

A continuación, se define un vector numérico y se realizan operaciones con él. Observa las diferencias entre la clase de los resultados que se obtienen (número o vector).

r1 <- c(2, 3, 3, 5, 2, 1, 4)
2 * pi * r1

r2 <- 1:5
2 * pi * r2

sum(r1)
sqrt(r1)
round(sqrt(r1), 2)

r1^2
r1 + 1
r1 * 2

2.2.2.4 Selección de elementos en un vector

Los elementos de un vector se pueden seleccionar mediante diversos métodos. A continuación, se muestran algunos de ellos: un vector de índices, un vector de índices de los elementos que no se incluyen en el resultado, un vector de booleanos. En este último caso, se muestra cómo definirlo explícitamente o bien obtenerlo como resultado de una operación de comparación.

r1[2:4]
r1[c(1,3,6)]

1:length(r1)

r1 > 3
r1[r1 > 3]

s <- c(FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE)
r1[s]

s1 <- !s
r1[s1]

s2 <- r1 > 3

n <- c("María", "Carmen", "Guillermo", "Ana", "Sergio", 
       "Lara", "Lucas")
n[s2]

n[2]
n[-2]
n[-(2:4)]

2.2.2.5 Unión de vectores

Mediante la función c() se combinan elementos para formar un vector. Como se muestra a continuación, los elementos pueden ser también vectores. Como todos los elementos de un vector han de ser del mismo tipo, el tipo del resultado ha de ser el más general de los tipos de los vectores implicados, entendiendo por este, el tipo al que se puedan transformar el resto de tipos sin perder información.

(n2 <- c(n, n))

(n3 <- c(n, r1, s))
(n4 <- c(r1, s, n))
(n5 <- c(r1, s))

2.2.3 Estructuras de datos

2.2.3.1 Vector

A continuación, podemos ver los valores y tamaño por defecto de un vector cuando se crea, según los parámetros que indiquemos o si lo creamos implícitamente; también se muestran distintas posibilidades que ofrece la función rep() para crear vectores.

(v1 <- vector(mode="numeric", length = 7))

(v2 <- vector(length = 7))

(v3 <- vector(mode="numeric"))
v3[4] <- 1
v3

rep(0, times = 7)
rep(1:4, times = 2)
rep(1:4, each = 2)       
rep(1:4, times = c(2,1,3,2))
rep(1:4, each = 2, len = 6)    
rep(1:4, each = 2, times = 3)  

2.2.3.2 Data frame

Podemos crear un data frame a partir de vectores, a los que les asignamos nombre dentro de la estructura: son las columnas del data frame, como se muestra a continuación. Un data frame puede tener tantas columnas como deseemos (no necesariamente dos como en los ejemplos siguientes). Podemos acceder a las columnas mediante su nombre o bien su posición en la estructura. Los vectores que forman las columnas pueden no tener el mismo número de elementos, siempre que el mayor sea múltipo del resto.

n
r1
(d <- data.frame(persona = n, puntos = r1))

d$persona
d$puntos
colnames(d)
colnames(d) <- c("person", "score")
d$score
d[, "score"]
d[, 2]

d[2,]
d[2, 2]
d[2:5, ]
d[-c(1,6,7), ]
d[2:5, 2]

n2
(d2 <- data.frame(persona = n2, puntos = r1))
(d3 <- data.frame(persona = n2, puntos = c(1, 2)))

# Error: arguments imply differing number of rows: 14, 3
# (d4 <- data.frame(persona = n2, puntos = c(1, 2, 3))) 

2.2.3.3 Matrix

A continuación, se muestran distintas formas de definir una matriz (matrix), según sus dimensiones y el orden de creación (por filas o por columnas). Asimismo, podemos ver cómo se le pueden asignar nombres a las filas y columnas de la matriz, y acceder a los elementos por nombre o por posición.

matrix(ncol = 2, nrow = 0)
matrix(ncol = 2, nrow = 1)
matrix(1:6)
matrix(1:6, ncol = 2)
matrix(1:6, ncol = 2, byrow = TRUE)
(m <- matrix(1:6, nrow = 3))

colnames(m) <- c("a", "b")
rownames(m) <- c("x", "y", "z")
m
m[, 2]
m[, "b"]
m[2, ]
m["y", ]
m["y", 2]

2.2.3.4 Factor

Cuando incluimos un vector de strings en un data frame, lo trata como un factor: considera los valores distintos (levels) y representa los datos en función de esos valores, como se muestra a continuación. Por defecto, asocia un número a cada nivel pero se puede indicar explícitamente que lo trate como string (mediante stringsAsFactors). Si definimos explícitamente los niveles a tener en cuenta en el factor, no se consideran los valores no incluidos entre los niveles.

(d2 <- data.frame(persona = n2, puntos = r1))
d2$persona

(d3 <- data.frame(persona = n2, puntos = r1, 
                  stringsAsFactors = FALSE))
d3$persona

(f <- factor(n2))
(f2 <- factor(n2, levels = c("Lara", "Ana")))

2.2.3.5 List

Una lista (list) puede estar compuesta por elementos de distinto tipo y número de instancias, como se muestra a continuación. A los elementos se les puede asignar nombres y podemos acceder a ellos por nombre o por posición.

(l <- list(n2, m, d, 2))
l[[2]][1,]

(l2 <- list(nombres = n2, matriz = m, df = d))
l2[[2]]
l2$matriz
l2[["matriz"]]

2.2.4 Control de flujo

2.2.4.1 Iteraciones

A continuación, se muestran distintas formas de iterar sobre una estructura de ejemplo. También se puede ver que se puede imprimir sin necesidad de iterar por ella.

for (nombre in n) {
  print(nombre)
}

for (i in 1:length(n)) {
  print(n[i])
}

i <- 1
while (i <= length(n)) {
  print(n[i])
  i <- i + 1
}

print(n)

2.2.4.2 Ejecución condicional

En lo que se refiere a la ejecución condicional, además de la construcción if-else, común en los lenguajes de programación, es destacable la posibilidad de tratar todos los elementos de un vector directamente mediante ifelse, como se muestra a continuación.

a <- 2
if (a == 3) {
  print(a)
} else {
  print(a*2)
}

ifelse(n == 'Ana', 1, 0)

ifelse(r1 < 2, 0, 1)

2.2.5 Funciones

2.2.5.1 Función con parámetros por defecto

A continuación, se muestra la definición y uso de una función con un parámetro por defecto. Las funciones devuelven el último valor calculado, aunque se puede indicar explícitamente el resultado mediante return().

f <- function(a, b = 1) {
  a + b
}

f(2)
f(2, 3)

2.2.5.2 Función a la que se pasa otra función como parámetro

Se pueden definir funciones a las que se pasa como parámetro otra función y, dentro de la función, invocar a la función que se ha pasado, como se hace a continuación.

g <- function(a) {
  return(a + 1)
}
g(2)

h <- function(k, a) {
  k(a)
}
h(g, 2)
h(f, 2)

2.2.5.3 Ámbito de las variables

Desde una función se puede acceder a una variable global pero esta no se puede modificar desde la función, ni directamente, ni pásandola como parámetro, como se muestra a continuación.

a <- 2

p <- function(b) {
  b <- b + a
  a <- 1
  return(b)
}

p(a)
a

2.2.6 Leer y escribir estructuras de datos

2.2.6.1 Guardar un data frame en un archivo y volverlo a leer

Cuando trabajamos con archivos, es práctico configurar la carpeta de trabajo mediante setwd()20 porque, de esta forma, basta con indicar el nombre del archivo. También se puede observar en el fragmento de código siguiente que, al escribir y leer un data frame, la estructura que se lee no coincide exactamente con la escrita: se le ha añadido una columna adicional con el número de línea. Si abrimos el archivo generado, se puede comprobar que la columna se ha generado al guardar los datos. Si queremos que no se almacene el número de fila lo tendremos que indicar al guardar el data frame, se hace mediante el parámetro row.names. Se puede observar que la clase del resultado que obtenemos es un data frame.

# setwd("datos/r")

d
write.csv(d, "marcador.csv")
(res <- read.csv("marcador.csv", header = TRUE, sep = ","))
res$person
class(res)

2.2.6.2 Guardar y leer un data frame, e interperetar strings como factores

A continuación, escribe y lee el data frame con el mismo número de columnas. También se indica que interprete como string los valores de los factores que lea.

write.csv(d, "marcador.csv", row.names = FALSE)
(res <- read.csv("marcador.csv", header = TRUE, sep = ",", 
                 stringsAsFactors = FALSE))
res$person

2.2.6.3 Guardar y leer una matriz

En el caso de guardar y leer una matriz, si le hemos dado nombre a las filas, nos interesa guardar sus nombres y recuperarlos al leer los datos. Se puede observar que leemos un data frame, pero lo podemos transformar en una matriz eliminando la columna correspondiente al nombre de las filas y haciendo una conversión de tipos.

m
write.csv(m, "matriz.csv")
(mat <- read.csv("matriz.csv", header = TRUE, sep = ","))
class(mat)
rownames(mat) <- mat[, 1]
(mat <- as.matrix(mat[, -1]))
class(mat)

2.2.7 Plots básicos

2.2.7.1 Diagrama de dispersión e histograma

La función genérica para representar gráficamente objetos en R es plot(). Como se muestra a continuación, en este caso, estamos representando puntos. La forma de representación de los puntos se determina mediante el parámetro pch21. También podemos utilizar otras funciones para obtener otros tipos de representación, como hist(), a continuación.

x1 <- rnorm(100)
y1 <- rnorm(100)

plot(x1, y1, pch=16, col='blue')

hist(x1)

2.2.7.2 Línea con puntos

A continuación, se muestra cómo usar la misma función plot() para representar los puntos unidos mediante una línea. También se pueden representar puntos en el mismo gráfico.

x2 <- seq(0, 2*pi, len=100)
y2 <- sin(x2)

plot(x2, y2, type='l', lwd=2, col='darkgreen', ylim=c(-1.2, 1.2))
y2r <- y2 + rnorm(100, 0, 0.1)
points(x2, y2r, pch='.', col='darkred')

2.2.7.3 Polígonos y plots múltiples

En el ejemplo siguiente, se divide la superficie de representación en dos partes mediante la función par(), una fila y dos columnas para poder mostrar conjuntamente dos figuras. Posteriormente, cuando se ha hecho la representación, se vuelve a dejar una sola columna mediante la misma función. En las dos representaciones se muestran los mismos datos, la diferencia entre ellas se produce por los parámetros asp (para mantener la proporcionalidad de la figura) y type (para no representar los datos propiamente dichos, sino establecer el área de representación que requieren).

# x2 <- seq(0, 2*pi, len=100)
# y2 <- sin(x2)
y3 <- cos(x2)

par(mfrow=c(1,2))

plot(y2, y3, asp=1)
polygon(y2, y3, col='lightgreen')

plot(y2, y3, type='n')
polygon(y2, y3, col='lightgreen')

par(mfrow=c(1,1))

2.2.7.4 Rectángulos, colores y transparencia

En el siguiente ejemplo, se muestra el uso de colores y la posibilidad de definir niveles de transparencia: se define mediante el cuarto parámetro de la función rgb(), que es opcional.

par(mfrow=c(1,2))

plot(c(-1.5,1.5),c(-1.5,1.5),asp=1, type='n')
rect(-0.5,-0.5,0.5,0.5, border=NA, col=rgb(0,0.5,0.5))
rect(0,0,1,1, col=rgb(1,0.5,0.5))

plot(c(-1.5,1.5),c(-1.5,1.5),asp=1, type='n')
rect(-0.5,-0.5,0.5,0.5, border=NA, col=rgb(0,0.5,0.5,0.7))
rect(0,0,1,1, col=rgb(1,0.5,0.5,0.7))

par(mfrow=c(1,1))

2.2.8 Paquetes

Instalar package.

Figura 2.3: Instalar package.

La funcionalidad básica de R se amplía mediante paquetes (packages) que podemos añadir a la instalación. Los paquetes instalados se pueden ver desde la pestaña Packages de la zona inferior derecha de la ventana (figura 2.3). Si necesitamos usar un paquete que no está instalado aún, pulsando sobre el botón Install podemos indicar que se instale.

Acceso a la información de un package.

Figura 2.4: Acceso a la información de un package.

Un paquete puede contener funciones y datos. Podemos acceder a la información de un paquete pulsando sobre el primer icono que aparece a la derecha de su nombre y versión, en la pestaña Packages (figura 2.4). Para utilizar el contenido de un paquete fácilmente, tenemos que indicar que se cargue, esto se hace mediante la función library().

2.3 Representación de capas

En el resto del documento, basándonos en las operaciones básicas presentadas en los ejemplos anteriores, vamos a usar R como un SIG.

2.3.1 Leer capas

En el ejemplo siguiente, comenzamos cargando el paquete sf. Hay muchos paquetes para trabajar con información geográfica. El paquete sp se usaba frecuentemente, sf está ocupando su lugar. Muchos paquetes, inicialmente basados en sp, se han adaptado a sf. Todavía quedan paquetes basados en sp que es posible que nos interese usar: podemos usarlos convirtiendo los datos de la estructura sf a sp.

library(sf)

censo <- st_read("datos/Granada/municipios censo GR.shp")
class(censo)
summary(censo)

hidro <- st_read("datos/Granada/red hidrografica GR.shp")
summary(hidro)

library(sp)
censo_sp <- as(censo, Class = "Spatial")
# funciones de sp.
class(censo_sp)
summary(censo_sp)

censo_sf <- st_as_sf(censo_sp)
class(censo_sf)
summary(censo_sf)

Mediante la función st_read() del paquete sf leemos una capa de la carpeta de trabajo. La función class() nos muestra que hemos leído un data frame. La función summary() nos muestra un resumen de las columnas del data frame. Las columnas son las mismas que muestra QGIS en la tabla de atributos pero, además, tenemos la columna geometry que es la que contiene los datos geográficos de la capa. Para representar solo la geometría de una capa, esta es la columna que debemos utilizar explícitamente.

2.3.2 Representar capas

La función st_geometry() del paquete sf nos devuelve el componente geometry de una capa22. Representamos la capa mediante la función plot(). Al resultado podemos añadir otras capas mediante la misma función, pero utilizando el parámetro add para indicarlo. El resultado se muestra en la zona inferior derecha de la ventana, en la pestaña Plots. Podemos exportarlo a distintos formatos mediante la opción Export.

plot(st_geometry(censo))
plot(st_geometry(hidro), col = "blue", add = TRUE)

2.3.3 Cambiar el color y otros elementos de una capa

Para cambiar la representación de una capa debemos representarla de nuevo. La función plot() ofrece muchas posibilidades, algunas de ellas se muestran a continuación: hemos cambiado el color, el grosor de las líneas y añadido ejes a la representación.

plot(st_geometry(censo), col = "lightgreen", lwd = 1.5, axes = TRUE)

2.3.4 Importar una imagen desde la Web

Vamos a importar una imagen desde OpenStreetMap23. Hay un paquete R llamado OpenStreetMap que ofrece esta funcionalidad.

library(OpenStreetMap)

censo_cg <- st_transform(censo, 4230)
summary(censo_cg)

# rectángulo más pequeño que incluye la capa
sup_izq <- as.vector(cbind(st_bbox(censo_cg)['ymax'],
                           st_bbox(censo_cg)['xmin']))
inf_der <- as.vector(cbind(st_bbox(censo_cg)['ymin'],
                           st_bbox(censo_cg)['xmax']))

mapa_osm <- openmap(sup_izq, inf_der, type = "bing")
censo_osm <- st_transform(censo_cg, osm())

plot(mapa_osm)
plot(st_geometry(censo_osm), add = TRUE, lwd = 1.2)

La función de OpenStreetMap que obtiene un mapa de una zona determinada es openmap(). Si miramos su definición, trabaja con la latitud y longitud. Deberemos transformar la capa que queramos representar a un CRS que permita obtener directamente estos datos: por ejemplo, el de código EPSG 4230. La transformación se puede llevar a cabo mediante la función st_transform().

A partir de esta representación de la capa, obtenemos los vértices del rectángulo más pequeño que la incluye. Con dos de esos vértices, mediante la función openmap() obtenemos el mapa de la Web, el tipo de mapa a importar se define mediante el parámetro type. Como vamos a representar el mapa obtenido conjuntamente con la capa de partida, vamos a transformar la capa para que tenga el mismo CRS que OpenStreetMap mediante las funciones st_transform(), que realiza la transformación, y osm(), que devuelve el CRS de OpenStreetMap.

Ejemplo con una de las capas y una imagen de OpenStreetMap.

Figura 2.5: Ejemplo con una de las capas y una imagen de OpenStreetMap.

En la figura 2.5, se muestra el resultado obtenido al presentar una de las capas con la capa obtenida de OpenStreetMap mediante el código de los ejemplos.

Ejercicio 2.3 Representa las capas de la provincia de Granada facilitadas junto a una capa obtenida de la Web (distinta a la utilizada en esta actividad). Define colores adecuados para los elementos representados en las capas.

Para documentar la realización del ejercicio, captura una pantalla completa de RStudio donde se muestre el último código ejecutado y el resultado obtenido en la ventana Viewer.

2.4 Explorar una capa

Si consultamos una capa mediante la función class(), podemos comprobar que es un data frame. Podemos acceder a esta estructura como se mostró con ejemplos en el apartado 2.2.3.2.

2.4.1 Acceder a elementos de una capa

En el ejemplo siguiente, en primer lugar, se muestran los datos de un municipio (accediendo al data frame que almacena los datos); a continuación, se representa gráficamente, de la misma forma que representamos toda la capa; por último, se representan varios municipios seleccionados en el data frame.

censo[censo$municipio == "Lanjarón", ]
plot(st_geometry(censo[censo$municipio == "Lanjarón", ]))

plot(st_geometry(censo[censo$municipio == "Lanjarón" |
                         censo$municipio == "Monachil" |
                         censo$municipio == "Dúrcal"|
                         censo$municipio == "Cáñar",]))

plot(st_geometry(censo[censo$municipio %in% c("Lanjarón",
                                              "Dilar",
                                              "Dúrcal",
                                              "Nigüelas",
                                              "Cáñar",
                                              "Bubión",
                                              "Pampaneira",
                                              "Soportújar",
                                              "Capileira",
                                              "Órjiva",
                                              "Vélez de Benaudalla",
                                              "El Pinar",
                                              "Lecrín"),]))

2.4.2 Tabla de atributos de una capa

La tabla de atributos de una capa es un data frame (la capa tiene más componentes). Podemos comprobarlo al mostrar todos sus datos, como se hace en el ejemplo siguiente. Podemos acceder a los componentes de varias formas, aunque no son equivalentes: por ejemplo, si accedemos a una columna mediante censo$Ce2018M obtenemos solo los datos de la columna; si lo hacemos mediante censo[, 'Ce2018T'] obtenemos adicionalmente la columna geometry. También podemos seleccionar varias columnas o combinar la selección de filas y columnas.

censo

censo$Ce2018T
censo[, 'Ce2018T']

censo[, c('Ce2015T', 'Ce2018T')]
censo[censo$municipio == "Lanjarón", c('Ce2015T', 'Ce2018T')]

2.5 Operaciones sobre la tabla de atributos

2.5.1 Lectura de una tabla en un data frame

Cada capa tiene su propia tabla de atributos pero también podemos tener solo tablas de atributos, sin elementos geométricos. A continuación, leemos la tabla con los datos del censo de los municipios de Granada para los últimos años.

library(readxl)

t <- read_excel("datos/Granada/2871gr.xlsx",
                col_names = TRUE,
                trim_ws = TRUE)
class(t)

Como veremos a continuación, podemos trabajar con estos datos para combinarlos con otras capas. Para este caso en particular, nos interesará combinar los datos del censo actuales con los almacenados en la capa de municipios, que contiene los datos del censo desde 2014 a 2018 asociados a información geográfica de los municipios.

2.5.2 Añadir un campo a la tabla de atributos

Se puede modificar la tabla de atributos de cualquier capa. Una de las modificaciones más frecuentes es añadir un campo nuevo definido a partir de otros campos. En este ejemplo, vamos a modificar la tabla de atributos de la capa que solo contiene esa tabla. Todas las tablas de atributos se modifican de la misma forma.

Añadir un campo a la tabla de atributos equivale a definir una nueva columna en un data frame: definimos el nombre de la nueva columna junto con la expresión para obtener sus valores.

t$Codigo <- substr(t$Municipio, 1, 5)
t[, 'Codigo']

2.5.3 Unir tablas de atributos

Para unir dos tablas de atributos, estas deben compartir algún campo con valores en común, los nombres de los campos utilizados para definir la unión no es relevante. La unión la realizamos mediante la función merge().

A la vista del resultado, podemos eliminar las columnas redundantes, en este caso, la columna Municipio, y, aunque no sea relevante el orden de las columnas en un data frame, podemos reordenar las columnas. Mediante la función dput() generamos el vector de nombres de columnas del data frame, utilizamos esa estructura para incluir solo las columnas que deseemos y reordenarlas si lo consideramos conveniente.

censo <- merge(x = censo, y = t, by.x = "cod_mun", by.y = "Codigo", all.x = TRUE)
censo

dput(names(censo))

censo <- censo[, c("cod_mun", "municipio", "provincia", "cod_ent", 
                   "shape_leng", "shape_area", "Superficie", 
                   "Ce2014T", "Ce2014H", "Ce2014M", 
                   "Ce2015T", "Ce2015H", "Ce2015M", 
                   "Ce2016T", "Ce2016H", "Ce2016M", 
                   "Ce2017T", "Ce2017H", "Ce2017M", 
                   "Ce2018T", "Ce2018H", "Ce2018M", 
                   "Ce2019T", "Ce2019H", "Ce2019M", 
                   "Ce2020T", "Ce2020H", "Ce2020M")]
censo

2.5.4 Consultas sobre la tabla de atributos

Supongamos que queremos seleccionar los municipios cuya población haya aumentado durante el último año. Podemos definir consultas de filtrado de filas sobre la tabla de atributos de una capa: son operaciones de filtrado sobre un data frame.

En el ejemplo siguiente, definimos el filtrado directamente y también definiendo un nuevo campo y, a continuación, definir el filtrado sobre él.

censo[(censo$Ce2020T - censo$Ce2019T) > 0, ]

censo$Incremento <- censo$Ce2020T - censo$Ce2019T
censo[censo$Incremento > 0, ]

2.5.5 Guardar una capa

Para guardar una capa en un archivo, indicamos la variable que contiene la capa y el archivo donde queremos que se guarde, como se muestra en el ejemplo siguiente. Según la extensión del archivo la función st_write() determina el formato a utilizar para almacenar los datos. El parámetro delete_dsn permite sobreescribir un archivo previamente guardado.

st_write(obj = censo, dsn = "datos/Granada/municipios censo GR jsamos.shp", delete_dsn = TRUE)

Ejercicio 2.4 Para la capa de municipios, actualiza la tabla de atributos con los datos del censo para los últimos años disponibles (los posteriores a 2018).

  • Una vez actualizada la tabla de atributos, define un campo llamado Incremento como el resultado de restar el total de la población del último año respecto al año anterior.

  • Selecciona los municipios que hayan aumentado su población en el último año.

  • Guarda la capa con todas las filas como un shapefile cuyo nombre incluya tu nombre de usuario de correo.

  • Guarda la capa filtrada como un shapefile cuyo nombre incluya tu nombre de usuario de correo y la palabra “filtro.”

Para documentar la realización del ejercicio, incluye el código R desarrollado y captura una pantalla completa de RStudio donde se muestre la tabla de atributos de cada una de las capas guardadas y se vean los nuevos campos añadidos. Se puede ver la tabla mediante la función View().

2.6 Añadir elementos a la representación las de capas

Se puede representar en una capa el valor de cualquier columna de la tabla de atributos. En particular, vamos a representar mediante etiquetas los nombres de los municipios y, creando un mapa de coropletas, un valor numérico.

2.6.1 Mostrar etiquetas

Las etiquetas las mostramos a continuación mediante la función text(). A esta función le tenemos que indicar, para cada fila, la posición donde escribir el texto y el campo que contiene el texto a escribir. Para determinar automáticamente las coordenadas, utilizamos la función st_centroid() que devuelve el centroide de la representación de cada municipio; a partir del centoide, mediante la función st_coordinates() se obtienen sus coordenadas. El texto que se representa es el contenido en el campo municipio. Mediante el resto de parámetros se indica la posición relativa del texto respecto a centroide y su tamaño.

seleccion <- "Lanjarón"
municipio_sel <- censo[censo$municipio == seleccion,]

plot(st_geometry(censo), axes = TRUE, main = seleccion)
plot(st_geometry(municipio_sel), col = "lightgreen", add = TRUE)

text(
  st_coordinates(st_centroid(st_geometry(censo))),
  labels = censo$municipio,
  pos = 3,
  cex = 0.5
)

A continuación, se muestra una forma alternativa de obtener un mapa con las etiquetas de los nombres; en este caso, mediante las funciones del paquete tmap. En la primera línea, mediante la función tm_shape(), se indica qué se quiere representar; en las líneas siguientes, se incluyen más detalles o más datos de la representació24. En este caso, no se puede ejecutar línea a línea sino que se ejecuta conjuntamente como un todo.

library(tmap)

tm_shape(censo) +
  tm_fill("white") +
  tm_borders() +
  tm_text("municipio", size = 0.3) +
  tm_shape(municipio_sel) +
  tm_fill("lightgreen") +
  tm_borders(lwd = 2) +
  tm_text("municipio", size = 0.3) +
  tm_layout(
    frame = FALSE,
    title = seleccion,
    title.size = 1,
    title.position = c(0.5, "top")
  )

2.6.2 Representar información numérica

La función plot() representa directamente información numérica en forma de mapa de coropletas, basta con indicar explícitamente el campo de la tabla de atributos a utilizar en la representación, como se muestra a continuación.

plot(censo[, "Incremento"], main = "Granada")

A continuación, se muestra cómo obtener un resultado similar al anterior pero con el paquete tmap.

tm_shape(censo) +
  tm_fill("Incremento") +
  tm_borders() +
  tm_layout(frame = FALSE, title = "Granada", 
            title.size = 1, title.position = c(0.35, "top"))

Ejercicio 2.5 Para la capa de municipios, una vez actualizada con los datos del censo:

  • Define un campo llamado tpc_inc que represente el porcentaje de incremento de la población del último año respecto al año anterior.

  • Representa el mapa mediante coropletas basadas en el campo tpc_inc para todos los municipios de la provincia.

Define etiquetas para alguna de las capas.

Para documentar la realización del ejercicio, incluye el código R desarrollado y captura una pantalla donde se muestre el resultado.

2.7 Generar la salida del proyecto

En los apartados siguientes se va a generar la salida del proyecto usando el paquete tmap. Para generar una imagen o un mapa también se puede usar el paquete sf.

En todos los casos funciona igual: se redirige la salida al tipo que se desee, se genera el mapa mediante las funciones necesarias y, cuando se haya acabado, se desactiva la salida para que se guarden los cambios.

2.7.1 Guardar como una imagen

En el ejemplo siguiente, se muestra cómo generar un archivo png (hay funciones similares para otros formatos gráficos, por ejemplo jpg()). En la función png() se pueden usar parámetros para indicar diversas características de la imagen que se genera. El proceso de generación se acaba al ejecutar la función dev.off(), entonces se guarda el archivo en la carpeta de trabajo.

png("selección.png") 

tm_shape(censo) +
  tm_fill("white") +
  tm_borders() +
  tm_text("municipio", size = 0.3) +
  tm_shape(municipio_sel) +
  tm_fill("lightgreen") +
  tm_borders(lwd = 2) +
  tm_text("municipio", size = 0.3) +
  tm_layout(
    frame = FALSE,
    title = seleccion,
    title.size = 1,
    title.position = c(0.5, "top")
  )
dev.off() 

2.7.2 Imprimir un mapa

A un mapa le podemos añadir el título, la escala, la flecha del norte, entre otros elementos. Podemos guardarlo como un gráfico o como un archivo en formato PDF, como se hace en el ejemplo siguiente. En las pruebas que he hecho, la resolución es mucho mejor en formato PDF que en cualquier formato gráfico, por eso se usa este formato.

pdf("selección.pdf")

tm_shape(censo) +
  tm_fill("Incremento") +
  tm_borders() +
  tm_text("municipio", size = 0.3) +
  tm_layout(
    frame = FALSE,
    title = "Granada",
    title.size = 1,
    title.position = c(0.35, "top")
  ) +
  tm_compass(type = "8star", position = c("right", "bottom")) +
  tm_scale_bar(
    breaks = c(0, 25, 50),
    text.size = 0.5,
    position = c("right", "bottom")
  )
dev.off()

2.7.3 Generar un mapa para la Web

Podemos generar un mapa para la Web usando funciones del paquete tmap. Funciona igual que para la generación de un archivo gráfico o en formato PDF: en este caso se usa la función tmap_mode() para indicar el inicio y el final del proceso de generación. En este caso, la generación se ha de llevar a cabo mediante funciones del propio paquete.

tmap_mode('view')

tm_shape(censo) +
  tm_fill(alpha = 0) +
  tm_borders() +
  tm_text("municipio", size = 0.5) +
  tm_shape(municipio_sel) +
  tm_fill("lightgreen") +
  tm_borders(lwd = 2) +
  tm_text("municipio", size = 0.5) +
  tm_layout(
    frame = FALSE,
    title = seleccion,
    title.size = 1,
    title.position = c(0.35, "top")
  )
tmap_mode('plot')
Guardar el resultado como una página Web en el disco local.

Figura 2.6: Guardar el resultado como una página Web en el disco local.

Nos permite guardar el resultado como una página Web local (figura 2.6), que podemos abrir con nuestro navegador.

Ejercicio 2.6 Genera una composición de impresión cuyo nombre sea tu nombre de usuario de correo que incluya un mapa, título, escala, leyenda y flecha del norte.

Genera un mapa para la Web a partir de la misma composición.

Para documentar la realización del ejercicio, incluye el código R desarrollado y captura las pantallas donde se muestren los resultados.

Bibliografía

Brundsdon, Chris, and Lex Comber. 2019. An Introduction to R for Spatial Analysis & Mapping (Second Edition). SAGE.
Lovelace, Robin, Jakub Nowosad, and Jannes Muenchow. 2019. Geocomputarion with R. CRC Press.
Matloff, Norman. 2011. The Art of R Programming: A Tour of Statistical Software Design. no starch press.

  1. https://www.r-project.org/↩︎

  2. https://www.rstudio.com/↩︎

  3. https://cran.r-project.org/doc/contrib/Owen-TheRGuide.pdf↩︎

  4. http://data.princeton.edu/R/introducingR.pdf↩︎

  5. https://cran.r-project.org/doc/contrib/Torfs+Brauer-Short-R-Intro.pdf↩︎

  6. https://cran.r-project.org/doc/manuals/R-intro.pdf↩︎

  7. Si seleccionamos varias líneas, al pulsar sobre el botón Run, se ejecutan todas. En esta actividad, nos interesará ejecutar línea a línea.↩︎

  8. Actualiza el parámetro que se pasa a esta función en el fragmento de código siguiente con tu carpeta de trabajo.↩︎

  9. En https://www.statmethods.net/advgraphs/parameters.html se pueden consultar los posibles valores que puede tomar este parámetro y el resultado obtenido en cada caso. También hay información de otros parámetros para definir el estilo de las líneas o los colores.↩︎

  10. Es recomendable usar esta función en lugar de acceder directamente al componente geometry del data frame porque el resultado obtenido con ambos métodos de acceso no siempre es idéntico.↩︎

  11. https://www.openstreetmap.org↩︎

  12. Esta es una manera de componer funciones que ofrece el paquete magrittr (https://magrittr.tidyverse.org/).↩︎