Итеративное извлечение повторяющихся словоформ из оборотов речи

#r #regex #for-loop

#r #регулярное выражение #for-цикл

Вопрос:

Я работаю над разговорными оборотами в разговоре. Меня интересуют слова, которые повторяются от предыдущего хода к следующему ходу:

 turnsX <- data.frame(
  speaker = c("A","B","A","B"),
  speech = c("let's have a look", 
             "yeah let's take a look",
             "yeah okay so where to start",
             "let's start here"), stringsAsFactors = F
)
 

Я хочу извлечь повторяющиеся словоформы. С этой целью я запустил for цикл, итеративно определяя каждый speech ход как шаблон регулярных выражений для следующего speech хода и str_extract вводя слова, которые повторяются от поворота к повороту:

 library(stringr)
pattern <- c()
extracted <- c()
for(i in 1:nrow(turnsX)){
  pattern[i] <- paste0(unlist(str_split(turnsX$speech[i], " ")), collapse = "|")
  extracted[i 1] <- str_extract_all(turnsX$speech[i 1], pattern[i])
}
 

Однако результат частично неверен:

 extracted
[[1]]
NULL

[[2]]
[1] "a"     "let's" "a"     "a"     "look" 

[[3]]
[1] "yeah" "a"    "a"   

[[4]]
[1] "start"

[[5]]
[1] NA
 

Правильный результат должен быть:

 extracted
[[1]]
NULL

[[2]]
[1] "let's"    "a"     "look" 

[[3]]
[1] "yeah"   

[[4]]
[1] "start"
 

Где ошибка? Как можно исправить код или какой другой подход существует, чтобы получить правильный результат?

Ответ №1:

Может быть, вы можете использовать Map и %in% .

 x <- strsplit(turnsX$speech, " ")
Map(function(y,z) y[y %in% z], x[-length(x)], x[-1])
#[[1]]
#[1] "let's" "a"     "look"
#
#[[2]]
#[1] "yeah"
#
#[[3]]
#[1] "start"
 

Ответ №2:

Вот базовый подход R, использующий Map :

 tmp <- strsplit(turnsX$speech, ' ')
c(NA, Map(intersect, tmp[-1], tmp[-length(tmp)]))

#[[1]]
#[1] NA

#[[2]]
#[1] "let's" "a"     "look" 

#[[3]]
#[1] "yeah"

#[[4]]
#[1] "start"
 

Комментарии:

1. Очень хорошее лаконичное решение, большое спасибо. Любой намек на то, где мой код идет не так?

2. Я могу предложить пару вещей. 1) Ваш for цикл начинается с 1:nrow(turnsX) того, что он должен быть 1:(nrow(turnsX) - 1) , поскольку для nrow(turnsX) нет i 1 позиции. 2) Использование регулярных выражений делает эту вещь немного более сложной, чем она есть на самом деле. Однако, если вы хотите исправить свой подход, вам нужно обернуть unique str_extract_all выходные данные, инициализировать extracted <- list() и изменить последнюю строку на extracted[[i]] <- unique(str_extract_all(turnsX$speech[i 1], pattern[i])[[1]])

3. Извините, вам не нужны unique границы слов, как показано в решении @jay.sf.

Ответ №3:

Вам нужны границы слов "\b"

 library(stringr)
pattern <- c()
extracted <- c()
for(i in 2:nrow(turnsX)){
  pattern[i - 1] <- paste0(unlist(str_split(turnsX$speech[i - 1], " ")), collapse = "|\b")
  extracted[i] <- str_extract_all(turnsX$speech[i], pattern[i - 1])
}
# [[1]]
# NULL
# 
# [[2]]
# [1] "let's" "a"     "look" 
# 
# [[3]]
# [1] "yeah"
# 
# [[4]]
# [1] "start"
 

Комментарии:

1. Я изменил выражение шаблона так, чтобы \b оно находилось по обе стороны от слов : paste0("\b(", paste0(unlist(str_split(kdd$Turn[i-1], " ")), collapse = "|"), ")\b") . В большем наборе данных это сильно меняет результаты.