Aquest document presenta una anàlisi de l’agenda d’activitats de les biblioteques i bibliobusos de la Xarxa de Biblioteques Municipals de la demarcació de Barcelona. El dataset d’activitats està disponible al Portal de Dades Obertes de la Diputació de Barcelona (DIBA): https://dadesobertes.diba.cat/datasets/biblioteques-i-bibliobusos-agenda-dactivitats
Farem servir el paquet jsonlite
per importar les dades
en format JSON i tidyverse
per a l’anàlisi i la
representació gràfica.
library(jsonlite)
library(tidyverse)
El dataset conté informació d’activitats dutes a terme a les biblioteques i bibliobusos de la DIBA. El següent codi descarrega els primers 5000 registres (dona problemes si intentem descarregar més registres amb una única consulta).
data1 <- fromJSON("https://do.diba.cat/api/dataset/actesbiblioteques_ca/pag-ini/1/pag-fi/5000/format/json")
En visualitzar aquests registres (amb les funcions
glimpse
, view
o print
, per
exemple), observem que la major part de la informació està continguda en
el camp elements
. Ens quedem, doncs, amb aquest camp i
descartem la resta.
data1 <- data1$elements
A continuació, seguim el mateix procediment amb la resta de registres disponibles al Portal:
data2 <- fromJSON("https://do.diba.cat/api/dataset/actesbiblioteques_ca/pag-ini/5001/pag-fi/10000/format/json")
data2 <- data2$elements
data3 <- fromJSON("https://do.diba.cat/api/dataset/actesbiblioteques_ca/pag-ini/10001/pag-fi/15000/format/json")
data3 <- data3$elements
Finalment, agrupem els tres datasets en un de sol. El convertim a
format tibble
i explorem la seva estructura:
data <- dplyr::bind_rows(data1, data2, data3)
data <- as_tibble(data)
glimpse(data)
## Rows: 13,653
## Columns: 35
## $ acte_id <chr> "agendabiblioteques409133631", "agendabiblioteques~
## $ titol <chr> "1r Concurs de puzzles de Collbató", "Ficció en jo~
## $ data_inici <chr> "2024-04-20 10:30:00", "2024-04-25 18:30:00", "202~
## $ data_fi <chr> "2024-04-20 23:59:59", "2024-04-25 23:59:59", "202~
## $ descripcio <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ imatge <list> "https://bibliotecavirtual.diba.cat/documents/210~
## $ acte_organitzadors <chr> "Collbató. Biblioteca Sibil·la Durfort", "Collbató~
## $ acte_url <chr> "https://bibliotecavirtual.diba.cat/detall-esdeven~
## $ telefon_contacte <list> "937 779 341", "937 779 341", "937 779 341", "937~
## $ email <list> "b.collbato@diba.cat", "b.collbato@diba.cat", "b.~
## $ grup_adreca <df[,5]> <data.frame[26 x 5]>
## $ url_general <chr> "https://bibliotecavirtual.diba.cat/detall-esde~
## $ colaboradors <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ rel_municipis <df[,18]> <data.frame[26 x 18]>
## $ rel_temes <df[,2]> <data.frame[26 x 2]>
## $ tags <list> <"Concurs", "Adults", "Collbató. Biblioteca Sibil·~
## $ categoria <list> <"concurs", "adults", "collbato_biblioteca_sib~
## $ rel_punt <chr> "biblioteca21134912", "biblioteca21134912", "bi~
## $ preu <chr> "", "", "", "", "", "", "", "", "", "", "", "", "~
## $ peu_imatge <list> <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <~
## $ durada <chr> "2 h", "1:30 h", "1:30 h", "1:30 h", "1 h", "", "1~
## $ observacions_horari <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ dies <chr> "", "", "", "", "", "", "", "", "", "", "", "", "~
## $ tipus <list> "Concurs", "Club de videojoc", "Club de videojoc",~
## $ public <list> "Adults", "Joves", "Joves", "Joves", "Infants", "A~
## $ observacions <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ rel_comarca <list> "11", "11", "11", "11", "11", "11", "11", "11", "~
## $ documentacio <list> [], [], [], [], [], [], [], [], [], [], [], [], [~
## $ assistents <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ aforament <chr> "", "", "", "", "", "", "", "", "", "", "", "", "~
## $ inscripcio <chr> "", "", "", "", "", "", "", "", "", "", "", "", "~
## $ cos <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ cercador_codi <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ url_inscripcions <chr> "", "", "", "", "", "", "", "", "", "", "", "", ""~
## $ id_secundari <chr> "lqo1", "lqo1", "lqo1", "lqo1", "lqo1", "lqo1", "l~
Podem observar que el dataset conté 13.653 registres organitzats en
35 columnes. No obstant, alguns d’aquests camps (descripció,
col·laboradors, etc.) estan buits. D’altra banda, cal “desanidar” les
variables en format dataframe (per exemple, rel_municipis
)
i en format llista (per exemple, tags
). Per “desanidar” ens
referim a separar en diferents columnes els diversos camps d’una
variable que actualment està en una única columna. Comencem per la
informació sobre els municipis:
data <- data %>%
unnest_wider(rel_municipis)
glimpse(data)
## Rows: 13,653
## Columns: 52
## $ acte_id <chr> "agendabiblioteques409133631", "agendabibli~
## $ titol <chr> "1r Concurs de puzzles de Collbató", "Ficci~
## $ data_inici <chr> "2024-04-20 10:30:00", "2024-04-25 18:30:00~
## $ data_fi <chr> "2024-04-20 23:59:59", "2024-04-25 23:59:59~
## $ descripcio <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ imatge <list> "https://bibliotecavirtual.diba.cat/docume~
## $ acte_organitzadors <chr> "Collbató. Biblioteca Sibil·la Durfort", "C~
## $ acte_url <chr> "https://bibliotecavirtual.diba.cat/detall-~
## $ telefon_contacte <list> "937 779 341", "937 779 341", "937 779 341~
## $ email <list> "b.collbato@diba.cat", "b.collbato@diba.ca~
## $ grup_adreca <df[,5]> <data.frame[26 x 5]>
## $ url_general <chr> "https://bibliotecavirtual.diba.cat/deta~
## $ colaboradors <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ ine <chr> "08069", "08069", "08069", "08069", "08069"~
## $ municipi_nom <chr> "Collbató", "Collbató", "Collbató", "Collba~
## $ municipi_nom_curt <chr> "Collbató", "Collbató", "Collbató", "Collba~
## $ municipi_article <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ municipi_transliterat <chr> "collbato", "collbato", "collbato", "collba~
## $ municipi_curt_transliterat <chr> "collbato", "collbato", "collbato", "collba~
## $ centre_municipal <chr> "41.56739770249645,1.8281992867969576", "41~
## $ grup_comarca <df[,2]> <data.frame[26 x 2]>
## $ grup_provincia <df[,2]> <data.frame[26 x 2]>
## $ grup_ajuntament <df[,9]> <data.frame[26 x 9]>
## $ municipi_escut <chr> "https://media.diba.cat/diba/municipis/img/~
## $ municipi_bandera <chr> "https://media.diba.cat/diba/municipis/i~
## $ municipi_vista <chr> "https://media.diba.cat/diba/municipis/i~
## $ ine6 <chr> "080692", "080692", "080692", "080692", ~
## $ nom_dbpedia <chr> "Collbató", "Collbató", "Collbató", "Collba~
## $ nombre_habitants <chr> "4849", "4849", "4849", "4849", "4849", "48~
## $ extensio <chr> "17.99", "17.99", "17.99", "17.99", "17.99"~
## $ altitud <chr> "388", "388", "388", "388", "388", "388", "~
## $ rel_temes <df[,2]> <data.frame[26 x 2]>
## $ tags <list> <"Concurs", "Adults", "Collbató. Biblioteca~
## $ categoria <list> <"concurs", "adults", "collbato_biblioteca_~
## $ rel_punt <chr> "biblioteca21134912", "biblioteca21134912",~
## $ preu <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ peu_imatge <list> <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, ~
## $ durada <chr> "2 h", "1:30 h", "1:30 h", "1:30 h", "1 h"~
## $ observacions_horari <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ dies <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ tipus <list> "Concurs", "Club de videojoc", "Club de vid~
## $ public <list> "Adults", "Joves", "Joves", "Joves", "Infa~
## $ observacions <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ rel_comarca <list> "11", "11", "11", "11", "11", "11", "11", "~
## $ documentacio <list> [], [], [], [], [], [], [], [], [], [], [],~
## $ assistents <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ aforament <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ inscripcio <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ cos <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ cercador_codi <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ url_inscripcions <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ id_secundari <chr> "lqo1", "lqo1", "lqo1", "lqo1", "lqo1", "lq~
Alguns camps, com el nom del municipi, es repeteixen múltiples
vegades, mentre que les comarques tornen a estar “anidades”
(grup_comarca
que conté el codi de la comarca i el seu
nom). Procedim a “desanidar” la informació sobre les comarques.
data <- data %>%
unnest_wider(grup_comarca)
glimpse(data)
## Rows: 13,653
## Columns: 53
## $ acte_id <chr> "agendabiblioteques409133631", "agendabibli~
## $ titol <chr> "1r Concurs de puzzles de Collbató", "Ficci~
## $ data_inici <chr> "2024-04-20 10:30:00", "2024-04-25 18:30:00~
## $ data_fi <chr> "2024-04-20 23:59:59", "2024-04-25 23:59:59~
## $ descripcio <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ imatge <list> "https://bibliotecavirtual.diba.cat/docume~
## $ acte_organitzadors <chr> "Collbató. Biblioteca Sibil·la Durfort", "C~
## $ acte_url <chr> "https://bibliotecavirtual.diba.cat/detall-~
## $ telefon_contacte <list> "937 779 341", "937 779 341", "937 779 341~
## $ email <list> "b.collbato@diba.cat", "b.collbato@diba.ca~
## $ grup_adreca <df[,5]> <data.frame[26 x 5]>
## $ url_general <chr> "https://bibliotecavirtual.diba.cat/deta~
## $ colaboradors <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ ine <chr> "08069", "08069", "08069", "08069", "08069"~
## $ municipi_nom <chr> "Collbató", "Collbató", "Collbató", "Collba~
## $ municipi_nom_curt <chr> "Collbató", "Collbató", "Collbató", "Collba~
## $ municipi_article <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ municipi_transliterat <chr> "collbato", "collbato", "collbato", "collba~
## $ municipi_curt_transliterat <chr> "collbato", "collbato", "collbato", "collba~
## $ centre_municipal <chr> "41.56739770249645,1.8281992867969576", "41~
## $ comarca_codi <chr> "11", "11", "11", "11", "11", "11", "11", "~
## $ comarca_nom <chr> "Baix Llobregat", "Baix Llobregat", "Baix L~
## $ grup_provincia <df[,2]> <data.frame[26 x 2]>
## $ grup_ajuntament <df[,9]> <data.frame[26 x 9]>
## $ municipi_escut <chr> "https://media.diba.cat/diba/municipis/img/~
## $ municipi_bandera <chr> "https://media.diba.cat/diba/municipis/i~
## $ municipi_vista <chr> "https://media.diba.cat/diba/municipis/i~
## $ ine6 <chr> "080692", "080692", "080692", "080692", "08~
## $ nom_dbpedia <chr> "Collbató", "Collbató", "Collbató", "Collba~
## $ nombre_habitants <chr> "4849", "4849", "4849", "4849", "4849", "48~
## $ extensio <chr> "17.99", "17.99", "17.99", "17.99", "17.99"~
## $ altitud <chr> "388", "388", "388", "388", "388", "388", "~
## $ rel_temes <df[,2]> <data.frame[26 x 2]>
## $ tags <list> <"Concurs", "Adults", "Collbató. Biblioteca~
## $ categoria <list> <"concurs", "adults", "collbato_biblioteca_~
## $ rel_punt <chr> "biblioteca21134912", "biblioteca21134912",~
## $ preu <chr> "", "", "", "", "", "", "", "", "", "", ~
## $ peu_imatge <list> <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>~
## $ durada <chr> "2 h", "1:30 h", "1:30 h", "1:30 h", "1 h"~
## $ observacions_horari <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ dies <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ tipus <list> "Concurs", "Club de videojoc", "Club de vi~
## $ public <list> "Adults", "Joves", "Joves", "Joves", "Infan~
## $ observacions <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ rel_comarca <list> "11", "11", "11", "11", "11", "11", "11", "~
## $ documentacio <list> [], [], [], [], [], [], [], [], [], [], []~
## $ assistents <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ aforament <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ inscripcio <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ cos <chr> "", "", "", "", "", "", "", "", "", "", ""~
## $ cercador_codi <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ url_inscripcions <chr> "", "", "", "", "", "", "", "", "", "", "",~
## $ id_secundari <chr> "lqo1", "lqo1", "lqo1", "lqo1", "lqo1", "lq~
El resultat final és un dataset, que hem anomenat data
,
amb 53 variables relatives a 13.653 activitats desenvolupades a la Xarxa
de biblioteques i bibliobusos de la DIBA.
Començarem fent un gràfic de línies que il·lustri l’evolució cronológica en l’organització d’activitats. Al dataset figura la data d’inici i de finalització de cada activitat, tot i que ambdues dates acostumen a coincidir (són activitats que s’inicien i finalitzen el mateix dia). Per crear el gràfic, farem servir la data d’inici. Una breu explicació del codi que farem servir:
as.POSIXct(fecha, format="%Y-%m-%d %H:%M:%S")
:
converteix la columna data_inici
a format
YYYY-MM-DD.
floor_date(..., "month")
: arrodoneix al primer dia
del mes (per exemple, assigna la data “1 de març” a totes les activitats
del mes de març).
count(mes)
: compta el nombre d’activitats per
mes.
chrono <- data %>%
mutate(mes = floor_date(as.POSIXct(data_inici, format="%Y-%m-%d %H:%M:%S"), "month")) %>%
count(mes)
glimpse(chrono)
## Rows: 59
## Columns: 2
## $ mes <dttm> 2004-05-01, 2014-04-01, 2014-05-01, 2014-06-01, 2014-10-01, 2014-~
## $ n <int> 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 1, 3, 6, 4, ~
Les dades fins a octubre de 2023 són anecdòtiques, amb menys de 10 activitats mensuals. El mateix passa després de juny de 2025 on majoritàriament s’observen errors en introduir l’any. Eliminem aquestes fileres i ens quedem amb les 13583 activitas realitzades entre octubre de 2023 i juny de 2025.
chrono <- chrono %>% slice(28:48)
glimpse(chrono)
## Rows: 21
## Columns: 2
## $ mes <dttm> 2023-10-01, 2023-11-01, 2023-12-01, 2024-01-01, 2024-02-01, 2024-~
## $ n <int> 73, 125, 65, 73, 150, 190, 1391, 1413, 1011, 568, 103, 604, 1569, ~
sum(chrono$n)
## [1] 13583
Representem l’evolució mensual en el nombre d’activitats amb una gràfica de línia. Sembla que les dades es van començar a introduir de manera sistemàtica l’abril de 2024. Es van realitzar unes 1400 activitats mensuals durant els mesos d’abril i maig d’aquell any. Més tard, l’activitat baixa durant l’estiu per remuntar a la tardor per arribar a superar les 1600 activitats el novembre de 2024. El nombre d’activitats es torna a reduir durant el desembre de 2024 i gener de 2025 i remunta al febrer i març (estem redactant això a inicis de març de 2025).
En la segona part del codi, fem el gràfic interactiu per poder veure el nombre d’activitats mensuals en passar el cursor.
fig <- ggplot(chrono, aes(x = mes, y = n)) +
geom_line(color = "black", size = 1) +
geom_point(color = "black", size = 3) +
labs(x = "Mes",
y = "Nombre d'activitats") +
theme_minimal()
library(plotly)
ggplotly(fig)
NOTA: es podria millorar el gràfic posant els noms dels mesos en lletra (“gener”, “febrer”, etc.).
Comencarem fent un recompte del nombre d’activitats realitzades a cada comarca. A més del valor absolut, calculem el percentatge que representen les activitats a cada comarca respecte del total.
data %>%
count(comarca_nom) %>%
mutate(percentatge = round(n / sum(n) * 100, 2))
Veiem que la comarca amb més activitats és el Baix Llobregat, per sobre, fins i tot, del Barcelonès (potser perquè Biblioteques de Barcelona no introdueix els seus actes a aquesta agenda?). Perquè el recompte sigui més informatiu, hauríem de posar en relació el nombre d’activitats amb la població de cada comarca. Les dades de població no figuren al dataset, però les poden obtenir a l’Idescat (https://www.idescat.cat/indicadors/?id=aec&n=15224).
pob.comarca <- read.table("poblacio.txt",
header = T,
sep = "\t")
pob.comarca
Hem de combinar dos datasets: el d’activitats i el de població. A
més, afegirem una columna amb el percentatge de població per comarca.
Hem d’incloure el paràmetre na.rm
perquè hi ha un petit
grup d’activitats al dataset que no tenen assignada una comarca (NA).
Aquesta “comarca” no té població i R no és capaç de calcular el
percentatge de població si hi ha fileres en blanc.
D’altra banda, hem tingut cura amb l’accentuació dels noms de les comarques en els dos datasets. Si hi hagués qualsevol discrepància, l’agrupació de les dades no funcionaria. A vegades és més fàcil fer servir un codi numèric en lloc de noms textuals que són més susceptibles de contenir errors.
act.comarca <- data %>%
count(comarca_nom) %>%
mutate(percentatge = round(n / sum(n) * 100, 2))
act.comarca.pob <- left_join(act.comarca, pob.comarca, by = c("comarca_nom" = "Comarca")) %>%
mutate(percentatge.poblacio = round((Poblacio / sum(Poblacio, na.rm = TRUE)) * 100, 2))
act.comarca.pob
Observem que, malgrat tenir el 40% de la població, menys del 18% de les activitats s’organitzen al Barcelonès (sempre suposant que les dades de l’agenda siguin completes). A l’extrem contrari, destaquen els casos del Baix Llobregat (20% de les activitats per al 14% de la població) i el Maresme (17% de les activitats per al 8% de la població).
Quins són els municipis del Baix Llobregat on s’organitzen més activitats?
data %>%
filter(comarca_nom == "Baix Llobregat") %>%
count(municipi_nom) %>%
arrange(desc(n))
I en el Barcelonès?
data %>%
filter(comarca_nom == "Barcelonès") %>%
count(municipi_nom) %>%
arrange(desc(n))
Els municipis del Maresme:
data %>%
filter(comarca_nom == "Maresme") %>%
count(municipi_nom) %>%
arrange(desc(n))
La següent taula mostra que hi ha fins a 250 biblioteques i bibliobusos que han organitzat almenys una activitat. S’observa que, entre les biblioteques més actives, se situen dues del Baix Llobregat i dues del Maresme, que eren les comarques més actives.
data %>%
count(acte_organitzadors) %>%
arrange(desc(n))
NOTA: a la taula anterior estaria bé posar la comarca. Cal comprovar que al dataset aquesta informació és homogènia (una biblioteca té sempre la mateixa comarca).
Al dataset, la informació relativa al públic està en format
list
. Primer la desanidem (funció unnest
) i,
després fem el recompte (funció count
).
La major part de les activitats estan adreçades al públic adult (44%).
data %>%
unnest(public) %>%
count(public) %>%
arrange(desc(n)) %>%
mutate(percentatge = round(n / sum(n) * 100, 2))
Igual que al cas anterior, la informació està en format
list
.
Les tipologies més freqüents són: tallers (19%), clubs de lectura (18%) i l’hora del conte (17%).
data %>%
unnest(tipus) %>%
count(tipus) %>%
arrange(desc(n)) %>%
mutate(percentatge = round(n / sum(n) * 100, 2))
NOTA: Aquí es podria fer una anàlisi més qualitativa a partir dels títols de les activitats. Per exemple, sobre què són els tallers? N’hi ha de manualitats (xapes, ex-libris, etc.) de ciència, de salut…
En primer lloc, descarreguem les dades de les comarques de Barcelona en formato GeoJSON. Caldria buscar alguna font oficial (Gencat) més actualitzada perquè ara veurem que aquí no està representat el Moianès, que es va crear el 2015. De moment, però, passarem amb això.
library(sf)
url <- "https://github.com/aariste/GeoJSON-Mapas/blob/master/comarques-barcelona.geojson?raw=true"
comarques <- st_read(url)
## Reading layer `OGRGeoJSON' from data source
## `https://github.com/aariste/GeoJSON-Mapas/blob/master/comarques-barcelona.geojson?raw=true'
## using driver `GeoJSON'
## Simple feature collection with 13 features and 3 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 1.35979 ymin: 41.19523 xmax: 2.778108 ymax: 42.32266
## Geodetic CRS: WGS 84
Per alguna raó, el Barcelonès i el Berguedà estan repetides. Eliminem els duplicats.
comarques <- comarques %>% slice(-c(6, 8))
De moment, representem el mapa de les comarques de Barcelona:
ggplot(comarques) +
geom_sf(fill = "lightblue", color = "black") +
geom_text(data = comarques, aes(x = st_coordinates(st_centroid(geometry))[,1],
y = st_coordinates(st_centroid(geometry))[,2],
label = nom_comar),
size = 3, color = "darkred") +
labs(title = "Mapa de les comarques de Barcelona") +
theme_minimal()
Ara cal afegir al dataframe amb la informació de les coordenades de les comarques la dada del nombre d’activitats bibliotecàries realitzades. Veureu que apareix a la columna “n”.
comarques <- left_join(comarques, act.comarca, by = c("nom_comar" = "comarca_nom"))
comarques
Finalment, tornem a fer el mapa, afegint el nombre d’activitats. On abans demanavem que el color de la comarca fos blau, ara demanem un color entre el groc (per a les comarques amb menys activitats) i vermell (per a les comarques amb més activitat).
ggplot(comarques) +
geom_sf(aes(fill = n), color = "black") +
scale_fill_gradient(low = "lightyellow", high = "red") +
labs(title = "Activitats de biblioteques per comarques", fill = "Activitats") +
geom_text(data = comarques, aes(x = st_coordinates(st_centroid(geometry))[,1],
y = st_coordinates(st_centroid(geometry))[,2],
label = nom_comar),
size = 3, color = "darkred") +
theme_minimal() +
theme(axis.line = element_blank(),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank())