Анализ аннотацй русскоязычных статей

Author

Орлова Анастасия

Published

1.12.2024

Задача работы: проанализировать метаданные статей, написанных на русском языке за 2024 год. Данные необходимо извлечь из базы данных OpenAlex.

Постановка проблемы и результаты: недавно исследователи обнаружили, что количество ошибок в аннотациях значительно возросло, в связи с чем я хочу проверить аннотации на встречающиеся ошибки. Так как в ходе работы я столкнулась с рядом ограничений (словарь из библиотеки hunspell распознает узкоспециализированный термины и имена собственные как слова с ошибкой). Я вручную посмотрела как часто встречаются ошибки в написании слова «научно-исследовательский». В 2024 году — всего два вхождения, однако на выборке за 21-23 год встречалось порядка 50, однако из-за большого объема данных мне не удалось провести работу с этой выборкой, преимущественно из-за ограниченных сроков (одна загрузка датасета заняла более часа). Таким образом в статьях за 2024 год ошибки все же встречаются, но необходимо избавиться от ограничений, мешающих провести полный анализ ошибок в аннотациях. Также из выборки аннотаций я хочу выделить наиболее часто встречающие слова. В дальнейшем эти данные могут позволить сделать срез актуальных направлений в журналах или дисциплинах и тематиках (в БД они присвоены каждой статье). В дальнейшем я планирую преодолеть ограничения, связанные с узкими терминами и встречаемость аннотаций на казахском языке и провести анализ ошибок анннотаций в разрезе по журналам и тематикам.

Недостатки работы: значительно затруднили работу: выгрузка данных по API, изучение документации новых для меня пакетов (openalexR, hunspell), а также большой объем анализируемых данных. Так загрузка и работа с метаданными статей за пятилетний и даже трехлетний период (более млн слов) занимает большое количество времени, в связи с чем выборку пришлось значительно сократить.

library(rvest)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()         masks stats::filter()
✖ readr::guess_encoding() masks rvest::guess_encoding()
✖ dplyr::lag()            masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(openalexR)
Thank you for using openalexR!
To acknowledge our work, please cite the package by calling `citation("openalexR")`.
To suppress this message, add `openalexR.message = suppressed` to your .Renviron file.
library(dplyr)
library(tidyr)
library(tidytext)
library(tokenizers)
library(wordcloud2)
library(ggplot2)
library(udpipe)
library(readxl)
library(xml2)
library(data.table)

Attaching package: 'data.table'

The following objects are masked from 'package:lubridate':

    hour, isoweek, mday, minute, month, quarter, second, wday, week,
    yday, year

The following objects are masked from 'package:dplyr':

    between, first, last

The following object is masked from 'package:purrr':

    transpose
library(httr)
library(jsonlite)

Attaching package: 'jsonlite'

The following object is masked from 'package:purrr':

    flatten
library(dplyr)
library(wordcloud2)
library(RColorBrewer)
library(gridExtra)

Attaching package: 'gridExtra'

The following object is masked from 'package:dplyr':

    combine
# загрузка научных публикаций на русском языке за 2024 год
df_works <- oa_request("https://api.openalex.org/works?filter=language:ru,publication_year:2024", verbose = TRUE)
Getting 239 pages of results with a total of 47609 records...
#преобразовывание данных в датафрейм
works <- works2df(df_works, abstract = TRUE)
#фильтрация полученного датафрейма
filtered <- works |> 
  select(title, ab, type) |> 
  filter(type == "article") |> 
  filter(!is.na(ab)) |> 
  rename (abstract = ab) |> 
  select(-type)
#удаление аннотаций на английском
filtered_text <- filtered[!grepl("[A-Za-z]", filtered$abstract), ]
filtered_text <- as_tibble(filtered_text)
#токенизируем аннотации, чтобы выявить наиболее популярные темы
abstract <- filtered_text |> 
  unnest_tokens("word", "abstract", to_lower = FALSE)
# удаляем текст после точки и саму точку
cleaned_abstract <- abstract |> 
  mutate(word = gsub("\\..*", "", word))
cleaned_abstract1 <- cleaned_abstract |>
  filter(!grepl("\\d", word))
# Убираем строки, где длина текста меньше или равна 1(это либо местоимения, либо ошибки токенизации)
df_cleaned <- cleaned_abstract1 |> 
  filter(nchar(word) > 3) 
#лемматизация
udpipe_download_model(language = "russian-syntagrus")
Downloading udpipe model from https://raw.githubusercontent.com/jwijffels/udpipe.models.ud.2.5/master/inst/udpipe-ud-2.5-191206/russian-syntagrus-ud-2.5-191206.udpipe to /Users/nastasyaorlova/Desktop/russian-syntagrus-ud-2.5-191206.udpipe
 - This model has been trained on version 2.5 of data from https://universaldependencies.org
 - The model is distributed under the CC-BY-SA-NC license: https://creativecommons.org/licenses/by-nc-sa/4.0
 - Visit https://github.com/jwijffels/udpipe.models.ud.2.5 for model license details.
 - For a list of all models and their licenses (most models you can download with this package have either a CC-BY-SA or a CC-BY-SA-NC license) read the documentation at ?udpipe_download_model. For building your own models: visit the documentation by typing vignette('udpipe-train', package = 'udpipe')
Downloading finished, model stored at '/Users/nastasyaorlova/Desktop/russian-syntagrus-ud-2.5-191206.udpipe'
russian_syntagrus <- udpipe_load_model(file = "russian-syntagrus-ud-2.5-191206.udpipe")
filtered_annotate <- udpipe_annotate(russian_syntagrus, df_cleaned$word, title = df_cleaned$word)

annotate_tbl <- as_tibble(filtered_annotate)
# ручной поиск слов с ошибками в строках
df_mistake <- annotate_tbl |>
  filter(grepl("научноисследовательский", lemma))
#Поиск наиболее часто встречающихся слов
most <- annotate_tbl |> 
  filter(!upos %in% "PRON") |> 
  count(lemma) |> 
  arrange(desc(n)) 
#облако наиболее частых слов 
pal <- c("#FFB3BA", "#FFDFBA", "#FFFFBA", "#BAFFBF", "#BAE1FF")
wordcloud2(data = most, 
           size = 1,                           
           color = pal,                        
           backgroundColor = "white",        
           shape = 'circle',                   
           minSize = 1)
#поиск наиболее часто встречающихся существительных и глаголов
most_n <- annotate_tbl  |> 
  filter(upos %in% "NOUN") |> 
  count(lemma) |> 
  arrange(-n) |> 
  slice_max(n, n = 20)

most_v <- annotate_tbl  |> 
  filter(upos %in% "VERB") |> 
  count(lemma) |> 
  arrange(-n) |> 
  slice_max(n, n = 20)
#визуализация
plot_nouns <- ggplot(most_n, aes(x = reorder(lemma, -n), y = n, fill = n)) +
  geom_bar(stat = "identity") +
  scale_fill_gradient(low = "lightblue", high = "darkblue") +  
  labs(title = "Наиболее частые существительные") +  
  theme_minimal(base_family = "Times New Roman") +  
  theme(text = element_text(size = 12), 
        axis.title.x = element_blank(),  
        axis.title.y = element_blank(),  
        axis.text.y = element_blank()) + 
  scale_y_continuous(labels = NULL) +  
  coord_flip()

plot_verbs <- ggplot(most_v, aes(x = reorder(lemma, -n), y = n, fill = n)) +
  geom_bar(stat = "identity") +
  scale_fill_gradient(low = "lightcoral", high = "red") +  
  labs(title = "Наиболее частые глаголы") +  
  theme_minimal(base_family = "Times New Roman") + 
  theme(text = element_text(size = 12), 
        axis.title.x = element_blank(),  
        axis.title.y = element_blank(),  
        axis.text.y = element_blank()) +  
  scale_y_continuous(labels = NULL) +  
  coord_flip()

grid.arrange(plot_nouns, plot_verbs, ncol = 2)

[Aria, M., Le T., Cuccurullo, C., Belfiore, A. & Choe, J. (2024), openalexR: An R-Tool for Collecting Bibliometric Data from OpenAlexThe R Journal, 15(4), 167-180]