Есть ли способ использовать dplyr Different(), чтобы рассматривать похожие значения как равные?

Я должен сделать анализ научных статей, опубликованных в списке из более чем 20 000 журналов. В моем списке более 450 000 записей, но с несколькими дубликатами (например, статья с более чем одним автором из разных учреждений появляется более одного раза).

Что ж, мне нужно посчитать количество статей в каждом журнале, но проблема в том, что разные авторы не всегда предоставляют информацию одинаково, и я могу получить что-то вроде следующей таблицы:

JOURNAL          PAPER
0001-1231        A PRE-TEST FOR FACTORING BIVARIATE POLYNOMIALS WITH COEFFICIENTS
0001-1231        A PRETEST FOR FACTORING BIVARIATE POLYNOMIALS WITH COEFFICIENTS
0001-1231        THE P3 INFECTION TIME IS W[1]-HARD PARAMETERIZED BY THE TREEWIDTH
0001-1231        THE P3 INFECTION TIME IS W-HARD PARAMETERIZED BY THE TREEWIDTH
0001-1231        COMPOSITIONAL AND LOCAL LIVELOCK ANALYSIS FOR CSP
0001-1231        COMPOSITIONAL AND LOCAL LIVELOCK ANALYSIS FOR CSP
0001-1231        AIDING EXPLORATORY TESTING WITH PRUNED GUI MODELS
0001-1231        DECYCLING WITH A MATCHING
0001-1231        DECYCLING WITH A MATCHING
0001-1231        DECYCLING WITH A MATCHING
0001-1231        DECYCLING WITH A MATCHING.
0001-1231        DECYCLING WITH A MATCHING
0001-1231        ON THE HARDNESS OF FINDING THE GEODETIC NUMBER OF A SUBCUBIC GRAPH
0001-1231        ON THE HARDNESS OF FINDING THE GEODETIC NUMBER OF A SUBCUBIC GRAPH.
0001-1232        DECISION TREE CLASSIFICATION WITH BOUNDED NUMBER OF ERRORS
0001-1232        AN INCREMENTAL LINEAR-TIME LEARNING ALGORITHM FOR THE OPTIMUM-PATH
0001-1232        AN INCREMENTAL LINEAR-TIME LEARNING ALGORITHM FOR THE OPTIMUM-PATH 
0001-1232        COOPERATIVE CAPACITATED FACILITY LOCATION GAMES
0001-1232        OPTIMAL SUFFIX SORTING AND LCP ARRAY CONSTRUCTION FOR ALPHABETS
0001-1232        FAST MODULAR REDUCTION AND SQUARING IN GF (2 M )
0001-1232        FAST MODULAR REDUCTION AND SQUARING IN GF (2 M)
0001-1232        ON THE GEODETIC NUMBER OF COMPLEMENTARY PRISMS
0001-1232        DESIGNING MICROTISSUE BIOASSEMBLIES FOR SKELETAL REGENERATION
0001-1232        GOVERNANCE OF BRAZILIAN PUBLIC ENVIRONMENTAL FUNDS: ILLEGAL ALLOCATION
0001-1232        GOVERNANCE OF BRAZILIAN PUBLIC ENVIRONMENTAL FUNDS: ILLEGAL ALLOCATION
0001-1232        GOVERNANCE OF BRAZILIAN PUBLIC ENVIRONMENTAL FUNDS - ILLEGAL ALLOCATION

Моя цель - использовать что-то вроде:

data%>%
distinct(JOURNAL, PAPER)%>%
group_by(JOURNAL)%>%
mutate(papers_in_journal = n())

Итак, у меня будет такая информация, как:

JOURNAL      papers_in_journal
0001-1231    6
0001-1232    7

Проблема в том, что вы можете увидеть некоторые ошибки в названии опубликованных статей. У некоторых есть «точка» в конце; некоторые имеют пробелы или заменяют символы; у некоторых есть другие незначительные вариации, такие как W[1]-HARD по сравнению с W-HARD. Итак, если я запускаю код как есть, у меня есть:

JOURNAL      papers_in_journal
0001-1231    10
0001-1232    10

Мой вопрос: есть ли способ учесть запас подобия при использовании Different() или аналогичной команды, чтобы у меня могло быть что-то вроде Different(JOURNAL, PAPER%whithin% 0,95)?

В этом смысле я хочу, чтобы команда учитывала:

A PRE-TEST FOR FACTORING BIVARIATE POLYNOMIALS WITH COEFFICIENTS
=
A PRETEST FOR FACTORING BIVARIATE POLYNOMIALS WITH COEFFICIENTS

THE P3 INFECTION TIME IS W[1]-HARD PARAMETERIZED BY THE TREEWIDTH
=
THE P3 INFECTION TIME IS W-HARD PARAMETERIZED BY THE TREEWIDTH

DECYCLING WITH A MATCHING
=
DECYCLING WITH A MATCHING.

etc.

Я полагаю, что нет такого простого решения с использованием Different(), и я не смог найти никаких альтернативных команд для этого. Так что, если это невозможно, и вы можете предложить любой алгоритм устранения неоднозначности, который я мог бы использовать, я также ценю.

Спасибо.


person André Brasil    schedule 06.04.2020    source источник
comment
Можете ли вы использовать нечеткое соответствие, такое как agrep, чтобы сначала свернуть второстепенные варианты?   -  person Greg    schedule 06.04.2020
comment
Вы уверены, что для второго журнала нет 8 статей?   -  person Edward    schedule 06.04.2020
comment
Правильно, Эдвард. Я неправильно подсчитал количество статей для второго журнала. Правильно было бы 8, учитывая сходство.   -  person André Brasil    schedule 06.04.2020


Ответы (2)


Один из вариантов — использовать agrep с lapply, чтобы найти индексы журнальных статей, отличающихся на 10 % (значение по умолчанию для agrep, которое можно изменить с помощью аргумента max.distance), а затем взять первую статью из каждого и векторизуйте его, используя sapply, получите индексы unique, длину вектора и оберните все это tapply, чтобы выбрать количество «непохожих» статей в каждом журнале.

  tapply(data$PAPER, data$JOURNAL, FUN=function(x) {
      length(unique(sapply(lapply(x, function(y) agrep(y, x) ), "[", 1))
     } )

# 0001-1231 0001-1232 
#         6         8 

Для версии dplyr, которая возвращает результаты в более приятном формате, я поместил приведенный выше код в функцию, затем использовал group_by(), а затем summarise().

dissimilar <- function(x, distance=0.1) {
  length(unique(sapply(lapply(x, function(y) 
     agrep(y, x, max.distance = distance) ), "[", 1)))
}

С определением «непохожего» в соответствии с документацией agrep.

library(dplyr)

data2 %>%
  group_by(JOURNAL) %>%
  summarise(n=dissimilar(PAPER))

# A tibble: 2 x 2
  JOURNAL       n
  <chr>     <int>
1 0001-1231     6
2 0001-1232     8

Однако для более крупного набора данных, например, содержащего тысячи журналов и более 450 000 статей, вышеуказанное будет довольно медленным (около 10-15 минут на моем Intel 2,50 ГГц). Я понял, что функция dissimilar без необходимости сравнивает каждую строку с каждой другой строкой, что не имеет особого смысла. В идеале каждую строку следует сравнивать только с самой собой и со всеми оставшимися строками. Например, первый журнал содержит 5 очень похожих статей в строках 8-12. Одно использование agrep в строке #8 возвращает все 5 индексов, поэтому нет необходимости сравнивать строки 9-12 с любыми другими. Поэтому я заменил lapply циклом for, и теперь процесс занимает всего 2-3 минуты с набором данных из 450 000 строк.

dissimilar <- function(x, distance=0.1) {
  lst <- list()               # initialise the list
  k <- 1:length(x)            # k is the index of PAPERS to compare with
  for(i in k){                # i = each PAPER, k = itself and all remaining
    lst[[i]] <- agrep(x[i], x[k], max.distance = distance) + i - 1 
                              # + i - 1 ensures that the original index in x is maintained
    k <- k[!k %in% lst[[i]]]  # remove elements which are similar
  }
  lst <- sapply(lst, "[", 1)  # take only the first of each item in the list
  length(na.omit(lst))        # count number of elements
}

Теперь расширьте исходный примерный набор данных, чтобы он содержал 450 000 записей, содержащих около 18 000 журналов, каждый из которых содержит около 25 статей.

n <- 45000
data2 <- do.call("rbind", replicate(round(n/26), data, simplify=FALSE))[1:n,]
data2$JOURNAL[27:n] <- rep(paste0("0002-", seq(1, n/25)), each=25)[1:(n-26)]

data2 %>%
  group_by(JOURNAL) %>%
  summarise(n=dissimilar(PAPER))

# A tibble: 18,001 x 2
   JOURNAL        n
   <chr>      <int>
 1 0001-1231      6 # <-- Same
 2 0001-1232      8
 3 0002-1        14
 4 0002-10       14
 5 0002-100      14
 6 0002-1000     13
 7 0002-10000    14
 8 0002-10001    14
 9 0002-10002    14
10 0002-10003    14

# ... with 17,991 more rows

Задача состоит в том, чтобы найти способ еще больше ускорить процесс.

person Edward    schedule 06.04.2020
comment
Уважаемый @Edward, это кажется отличным решением, но оно кажется очень требовательным с точки зрения обработки (я запускал скрипт около получаса, и он все еще работает). Есть ли способ ограничить выполнение функции внутри групп? Например, я могу группировать по журналам и по годам публикации, и скрипт будет сравнивать названия только среди статей в этих группах). Спасибо еще раз. - person André Brasil; 06.04.2020
comment
Да, я должен был протестировать этот код на гораздо большем наборе данных. На самом деле, мы можем существенно ограничить количество agrep сравнений, просматривая каждый журнал только с помощью tapply. Смотрите обновленный ответ. - person Edward; 07.04.2020

Вы захотите использовать пакет, предназначенный для обработки естественного языка. Попробуйте пакет Quanteda.

person hedgedandlevered    schedule 06.04.2020
comment
Интересное предложение, но не думаю, что в данном случае оно сработает, так как в списке есть публикации на нескольких языках. НЛП отлично подходит для контента только на английском языке, но для этого сравнения мне нужно что-то, что будет сравнивать комбинации символов, пытаясь найти сходство, не разбираясь в содержании. Спасибо. - person André Brasil; 06.04.2020
comment
Пакет LexNLP также имеет некоторую многоязычную поддержку (но только для python). Для произвольных кластеров символов вам нужно изучить слово в век - person hedgedandlevered; 07.04.2020