Chargement des packages nécessaires et des données

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

Nettoyage des données

Il y a plusieurs choses à nettoyer :

Dans ce qui suit, nous nettoyons data afin de produire 2 data.frames supplémentaires :

(Très) brève introduction aux regex

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:

  • extraire le premier mot, ainsi que le dernier
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]+(?=\\.$)")
  • extraire la date
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:]+")
  • remplacer la dernière partie du texte après les deux points
nouvelle_phrase <- str_replace(phrase_stupide, ":.*", ": mais je m'en souviens plus.")
  • supprimer les mots de moins de 2 lettres ou moins
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…

Nettoyage des références

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