R: найдите конкретную строку рядом с другой строкой с помощью цикла for

#r #for-loop #corpus

#r #for-цикл #corpus

Вопрос:

У меня есть текст романа в одном векторе, он был разделен по словам novel.vector.words Я ищу все экземпляры строки «blood of». Однако, поскольку вектор разделен на слова, каждое слово является отдельной строкой, и я не знаю, как искать смежные строки в векторе.

У меня есть базовое понимание того, что делают циклы for, и, следуя некоторым инструкциям из учебника, я могу использовать этот цикл for для таргетинга на все позиции «blood» и контекст вокруг него, чтобы создать отображение KWIC с разделением на вкладки (ключевые слова в контексте).

 node.positions <- grep("blood", novel.vector.words)

output.conc <- "D:/School/U Alberta/Classes/Winter 2019/LING 603/dracula_conc.txt"
cat("LEFT CONTEXTtNODEtRIGHT CONTEXTn", file=output.conc) # tab-delimited header

#This establishes the range of how many words we can see in our KWIC display
context <- 10 # specify a window of ten words before and after the match

for (i in 1:length(node.positions)){ # access each match...
  # access the current match
  node <- novel.vector.words[node.positions[i]]
  # access the left context of the current match
  left.context <- novel.vector.words[(node.positions[i]-context):(node.positions[i]-1)]
  # access the right context of the current match
  right.context <- novel.vector.words[(node.positions[i] 1):(node.positions[i] context)]
  # concatenate and print the results
  cat(left.context,"t", node, "t", right.context, "n", file=output.conc, append=TRUE)}
  

Однако я не уверен, как это сделать, это использовать что-то вроде оператора if или что-то в этом роде, чтобы фиксировать только экземпляры «blood», за которыми следует «of». Нужна ли мне другая переменная в цикле for? В основном я хочу, чтобы это делалось для каждого найденного экземпляра «blood», я хочу посмотреть, является ли слово, которое сразу следует за ним, «of». Я хочу, чтобы цикл нашел все эти экземпляры и сообщил мне, сколько их в моем векторе.

Ответ №1:

Вы можете создать индекс, используя dplyr::lead для соответствия ‘of’ следующей ‘blood’:

 library(dplyr)

novel.vector.words <- c("blood", "of", "blood", "red", "blood", "of", "blue", "blood")

which(grepl("blood", novel.vector.words) amp; grepl("of", lead(novel.vector.words)))

[1] 1 5
  

В ответ на вопрос в комментариях:

Это, безусловно, можно было бы сделать с помощью подхода, основанного на цикле, но нет особого смысла заново изобретать колесо, когда уже есть пакеты, лучше спроектированные и оптимизированные для выполнения тяжелой работы в задачах интеллектуального анализа текста.

Вот пример того, как с помощью tidytext пакета определить, как часто слова «кровь» и «из» встречаются с интервалом в пять слов друг от друга в «Дракуле» Брэма Стокера.

 library(tidytext)
library(dplyr)
library(stringr)

## Read Dracula into dataframe and add explicit line numbers
fulltext <- data.frame(text=readLines("https://www.gutenberg.org/ebooks/345.txt.utf-8", encoding = "UTF-8"), stringsAsFactors = FALSE) %>%
  mutate(line = row_number())

## Pair of words to search for and word distance
word1 <- "blood"
word2 <- "of"
word_distance <- 5

## Create ngrams using skip_ngrams token
blood_of <- fulltext %>% 
  unnest_tokens(output = ngram, input = text,  token = "skip_ngrams", n = 2, k = word_distance - 1) %>%
  filter(str_detect(ngram, paste0("\b", word1, "\b")) amp; str_detect(ngram, paste0("\b", word2, "\b"))) 

## Return count
blood_of %>%
  nrow

[1] 54

## Inspect first six line number indices
head(blood_of$line)

[1]  999 1279 1309 2192 3844 4135
  

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

1. Спасибо, это намного проще, чем цикл for. Мне все еще интересно, сделали бы вы это с помощью цикла for, поэтому я собираюсь оставить вопрос немного открытым, и я приму ваш ответ, если больше ничего не услышу. Однако у меня есть связанный с этим вопрос: что, если бы я хотел найти «of» в определенном диапазоне значений «blood»? Произнесите любое слово в пределах 5 слов слева и 5 слов справа.