R est un langage de programmation flexible et en constante évolution grâce à un immense échantillon de “packages”/paquets qui permettent d’importer des fonctions.
Un ensemble de paquets quasi-indispensable sur R sont regroupés sous une sorte de méta-paquet, tidyverse. Plus particulièrement nous allons surtout utiliser les packages stringr et dplyr de tidyverse. On “charge” ensuite tidyverse pour pouvoir en utiliser les fonctions.
install.packages("tidyverse", dependencies = TRUE)
library(tidyverse)
Une autre manière d’utiliser les fonctions d’un package sans le charger est d’écrire tidyverse::la_fonction_utilisée. Dans ce tutoriel, tous les packages utilisés seront chargés, mais j’essaierai tout de même d’utiliser cette formulation pour que vous puissiez voir à quel package appartie chaque fonction.
Quand on a plusieurs packages à installer et charger, il peut être plus clair de mettre ces packages dans une liste, d’installer ces packages uniquement s’ils ne sont pas déjà installés, puis de les charger.
package_list <- c(
"tidyverse",
"here", # use for paths creation
"janitor", # useful functions for cleaning imported data
"biblionetwork" # creating edges
)
for (p in package_list) {
if (p %in% installed.packages() == FALSE) {
install.packages(p, dependencies = TRUE)
}
library(p, character.only = TRUE)
}
Le package here permet de manipuler plus aisément les sentiers de vos dossiers sur votre ordinateur. Cela est important quand vous importez et exportez des fichiers. Par exemple, la commande suivante vous permet de savoir où est votre “dossier de travail” actuel:
here::here() # on enregistre le sentier dans un "objet"
[1] "C:/Users/goutsmedt/Documents/MEGAsync/Research/R"
C:/Users/goutsmedt/Documents/MEGAsync/Research/R
Pour plus de simplicité, vous pouvez mettre les données transmises par mail, dsge_articles_sample.csv dans ce dossier. Vous n’avez alors plus qu’à les importer avec readr::read_csv2(here("dsge_articles_sample.csv")). Ou bien vous pouvez mettre les données dans le même dossier que le présent fichier et l’importer comme suite:
data <- readr::read_csv2("dsge_articles_sample.csv")
i Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
Rows: 500 Columns: 22
-- Column specification ---------------------------------------------------------------------------------------------------------------
Delimiter: ";"
chr (19): authors, title, source_title, volume, issue, art_no, page_start, page_end, doi, affiliations, authors_with_affiliations, ...
dbl (3): year, page_count, cited_by
i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
On peut désormais regarder à quoi ressemble nos données:
data
Il y a plusieurs choses à nettoyer :
auteurs par document. Pour certaines analyses, par exemple les réseaux de co-auteurs, il est préférable d’avoir une “table des auteurs” qui associe chaque auteur à une liste d’articles ;affiliations par article ainsi que plusieurs authors_with_affiliations. Cela nous permet de relier les auteurs à leurs affiliations, mais là encore nous devons le séparer en autant de lignes que d’auteurs (si je ne me trompe pas, il semble qu’il n’y ait qu’une seule affiliation par auteur dans ce jeu de données) ;références ;author_keywords et index_keywords si vous voulez l’utiliser.Dans ce qui suit, nous nettoyons data afin de produire 2 data.frames supplémentaires :
data.frame qui associe chaque article à la liste des références qu’il cite (un article a autant de lignes que le nombre de références citées) : c’est un tableau de “citations directes” ;Pour nettoyer les données nous allons utiliser un langage particulier, très rebutant au départ: les expressions régulières (regular expressions ou regex en anglais). Les regex permettent de repérer dans un ensemble de caractères certaines particularités. Vous pouvez par exemple ensuite supprimer, extraire, modifier ces particularités.
phrase_stupide <- "Nous somme en cours le vendredi 4 février et l'intervenant commence à nous parler de quelque chose d'étrange: les regex."
Donnons nous quelques tâches:
premier_mot <- stringr::str_extract(phrase_stupide, "^[A-z]{4}")
premier_mot <- stringr::str_extract(phrase_stupide, "^[A-z]+") # peut être simplifié
dernier_mot <- stringr::str_extract(phrase_stupide, "[A-z]+$") # ne marche pas
dernier_mot <- stringr::str_extract(phrase_stupide, "[A-z]+\\.$") # peut être amélioré
dernier_mot <- stringr::str_extract(phrase_stupide, "[A-z]+(?=\\.$)")
date <- str_extract(phrase_stupide, "[A-z]+ \\d [A-z]+") # ne marche pas pour le mois
date <- str_extract(phrase_stupide, "[A-z]+ \\d [:letter:]+")
nouvelle_phrase <- str_replace(phrase_stupide, ":.*", ": mais je m'en souviens plus.")
phrase_deconstruite <- str_remove_all(phrase_stupide, " [:letter:]{1,2} ")
phrase_deconstruite <- str_remove_all(phrase_stupide, "(?<= )[:letter:]{1,2}(?= )")
Maintenant, vous allez pouvoir tout comprendre au nettoyage des données qui suit. Ou peut-être pas…
La première chose à faire est de créer un identifiant pour les articles citant, ce qui nous permettra de faire le pont entre les articles citant et les références citées:
data <- data %>%
mutate(citing_id = paste0("A", 1:n()))
Extrayons d’abord la colonne references en gardant un identifiant pour l’article citant. Nous mettons chaque référence citée par un article sur une ligne séparée, en utilisant le fait que les références sont séparées par un point-virgule. Nous créons un identifiant pour chaque référence.
#' ## Extracting and cleaning references
references_extract <- data %>%
filter(! is.na(references)) %>% # enlever les articles qui n'ont pas de références
select(citing_id, references) %>%
separate_rows(references, sep = "; ") %>% # on met une référence par ligne
mutate(id_ref = 1:n()) %>% # on donne un identifiant aux références
as_tibble
references_extract
Notre but désormais est de pouvoir extraire les métadonnées de chaque référence: les auteurs, la date de publication, le titre, le journal, etc… L’objectif est d’avoir des informations relativement standardisées, pour pouvoir ensuite identifier quelles sont les références qui sont les mêmes. Une première étape est d’extraire les auteurs ainsi que l’année de publication. On enlève les auteurs de la référence principale et on stock l’info restante, qu’on va nettoyer, dans une colonne séparée.
extract_authors <- ".*[:upper:][:alpha:]+( Jr(.)?)?, ([A-Z]\\.[ -]?)?([A-Z]\\.[ -]?)?([A-Z]\\.)?[A-Z]\\."
extract_year_brackets <- "(?<=\\()\\d{4}(?=\\))"
extract_pages <- "(?<= (p)?p\\. )([A-Z])?\\d+(-([A-Z])?\\d+)?"
extract_volume_and_number <- "(?<=( |^)?)\\d+ \\(\\d+(-\\d+)?\\)"
cleaning_references <- references_extract %>%
mutate(authors = str_extract(references, paste0(extract_authors, "(?=, )")),
remaining_ref = str_remove(references, paste0(extract_authors, ", ")), # cleaning from authors
is_article = ! str_detect(remaining_ref, "^\\([:digit:]{4}"),
year = str_extract(references, extract_year_brackets) %>% as.integer)
cleaning_references
A partir de maintenant, il y a beaucoup trop d’étapes pour pouvoir tout détailler, mais la stratégie générale est de séparer les références suivant la position de l’année de publication: le plus simple à nettoyer (cleaning_articles) est quand la titre est avant la date de publication car cela permet d’extraire plus simplement le titre, et donc aussi, en général, le nom du journal qui va suivre la date de publication. On extrait également des informations utiles comme le volume, le numéro, les pages, et le DOI.
cleaning_articles <- cleaning_references %>%
filter(is_article == TRUE) %>%
mutate(title = str_extract(remaining_ref, ".*(?=\\(\\d{4})"), # pre date extraction
journal_to_clean = str_extract(remaining_ref, "(?<=\\d{4}\\)).*"), # post date extraction
journal_to_clean = str_remove(journal_to_clean, "^,") %>% str_trim("both"), # cleaning a bit the journal info column
pages = str_extract(journal_to_clean, extract_pages), # extracting pages
volume_and_number = str_extract(journal_to_clean, extract_volume_and_number), # extracting standard volument and number: X (X)
journal_to_clean = str_remove(journal_to_clean, " (p)?p\\. ([A-Z])?\\d+(-([A-Z])?\\d+)?"), # clean from extracted pages
journal_to_clean = str_remove(journal_to_clean, "( |^)?\\d+ \\(\\d+(-\\d+)?\\)"), # clean from extracted volume and number
volume_and_number = ifelse(is.na(volume_and_number), str_extract(journal_to_clean, "(?<= )([A-Z])?\\d+(-\\d+)?"), volume_and_number), # extract remaining numbers
journal_to_clean = str_remove(journal_to_clean, " ([A-Z])?\\d+(-\\d+)?"), # clean from remaining numbers
journal = str_remove_all(journal_to_clean, "^[:punct:]+( )?[:punct:]+( )?|(?<=,( )?)[:punct:]+( )?([:punct:])?|[:punct:]( )?[:punct:]+( )?$"), # extract journal info by removing inappropriate punctuations
first_page = str_extract(pages, "\\d+"),
volume = str_extract(volume_and_number, "\\d+"),
issue = str_extract(volume_and_number, "(?<=\\()\\d+(?=\\))"),
publisher = ifelse(is.na(first_page) & is.na(volume) & is.na(issue) & ! str_detect(journal, "(W|w)orking (P|p)?aper"), journal, NA),
book_title = ifelse(str_detect(journal, " (E|e)d(s)?\\.| (E|e)dite(d|urs)? "), journal, NA), # Incollection article: Title of the book here
book_title = str_extract(book_title, "[A-z ]+(?=,)"), # keeping only the title of the book
publisher = ifelse(!is.na(book_title), NA, publisher), # if we have an incollection article, that's not a book, so no publisher
journal = ifelse(!is.na(book_title) | ! is.na(publisher), NA, journal), # removing journal as what we have is a book
publisher = ifelse(is.na(publisher) & str_detect(journal, "(W|w)orking (P|p)?aper"), journal, publisher), # adding working paper publisher information in publisher column
journal = ifelse(str_detect(journal, "(W|w)orking (P|p)?aper"), "Working Paper", journal))
cleaned_articles <- cleaning_articles %>%
select(citing_id, id_ref, authors, year, title, journal, volume, issue, pages, first_page, book_title, publisher, references)
cleaning_non_articles <- cleaning_references %>%
filter(is_article == FALSE) %>%
mutate(remaining_ref = str_remove(remaining_ref, "\\(\\d{4}\\)(,)? "),
title = str_extract(remaining_ref, ".*(?=, ,)"),
pages = str_extract(remaining_ref, "(?<= (p)?p\\. )([A-Z])?\\d+(-([A-Z])?\\d+)?"), # extracting pages
volume_and_number = str_extract(remaining_ref, "(?<=( |^)?)\\d+ \\(\\d+(-\\d+)?\\)"), # extracting standard volument and number: X (X)
remaining_ref = str_remove(remaining_ref, " (p)?p\\. ([A-Z])?\\d+(-([A-Z])?\\d+)?"), # clean from extracted pages
remaining_ref = str_remove_all(remaining_ref, ".*, ,"), # clean dates and already extracted titles
remaining_ref = str_remove(remaining_ref, "( |^)?\\d+ \\(\\d+(-\\d+)?\\)"), # clean from extracted volume and number
volume_and_number = ifelse(is.na(volume_and_number), str_extract(remaining_ref, "(?<= )([A-Z])?\\d+(-\\d+)?"), volume_and_number), # extract remaining numbers
remaining_ref = str_remove(remaining_ref, " ([A-Z])?\\d+(-\\d+)?"), # clean from remaining numbers
journal = ifelse(str_detect(remaining_ref, "(W|w)orking (P|p)aper"), "Working Paper", NA),
journal = ifelse(str_detect(remaining_ref, "(M|m)anuscript"), "Manuscript", journal),
journal = ifelse(str_detect(remaining_ref, "(M|m)imeo"), "Mimeo", journal),
publisher = ifelse(is.na(journal), remaining_ref, NA) %>% str_trim("both"),
first_page = str_extract(pages, "\\d+"),
volume = str_extract(volume_and_number, "\\d+"),
issue = str_extract(volume_and_number, "(?<=\\()\\d+(?=\\))"),
book_title = NA) # to be symetric with "cleaned_articles"
cleaned_non_articles <- cleaning_non_articles %>%
select(citing_id, id_ref, authors, year, title, journal, volume, issue, pages, first_page, book_title, publisher, references)
# merging the two files.
cleaned_ref <- rbind(cleaned_articles, cleaned_non_articles)
#' Now we have all the references, we can do a bit of cleaning on the authors name,
#' and extract useful information, like DOI, for matching later.
cleaned_ref <- cleaned_ref %>%
mutate(authors = str_remove(authors, " Jr\\."), # standardising authors name to favour matching later
authors = str_remove(authors, "^\\(\\d{4}\\)(\\.)?( )?"),
authors = str_remove(authors, "^, "),
authors = ifelse(is.na(authors), str_extract(references, ".*[:upper:]\\.(?= \\d{4})"), authors), # specific case
journal = str_remove(journal, "[:punct:]$"), # remove unnecessary punctuations at the end
doi = str_extract(references, "(?<=DOI(:)? ).*|(?<=\\/doi\\.org\\/).*"),
pii = str_extract(doi, "(?<=PII ).*"),
doi = str_remove(doi, ",.*"), # cleaning doi
pii = str_remove(pii, ",.*"), # cleaning pii
)
cleaned_ref