R: gsub, только если отрицание происходит в пределах n букв перед целью

Я работаю над примитивным алгоритмом анализа речи. Теперь я хочу улучшить то, как он обрабатывает отрицания положительных/отрицательных утверждений. На данный момент я добавляю строку «НЕ_» только в том случае, если происходит прямое отрицание:

s_commentsOut$gsubContent <- gsub("not ","not NOT_",gsub("n't ","n't NOT_",s_commentsOut$lowCo))

Так например

"This is not good"

становится

"This is not NOT_good"

Теперь я хочу добиться того, чтобы «НЕ_» также добавлялось, когда между вектором целевых слов и отрицанием есть n символов, например:

targetList <- c("nice", "perfect", "good", "love")

Теперь с помощью приведенного выше списка следующая строка:

"This isn't a very good way"

должен стать

"This isn't a very NOT_good way"

Эта замена должна иметь место только в том случае, если отрицание происходит за n (например, 15) символов до цели, например. следующее не должно быть преобразовано (поскольку расстояние между целью и отрицанием равно > 15):

"This is not going to work. However you did this very nicely."

Я нашел следующие статьи SO: Отрицание нескольких символов перед шаблоном

Как заменить символ в строке, но только если он встречается в подстроке с разделителями?

Но я изо всех сил пытаюсь понять это правильно. Тем временем я помогаю себе удалять из текста такие строки, как "нравится", "ан", "а"...

Дополнительные тестовые фразы:

"Nottingham is the love of my life."

"This is good. Nottingham is a town."

"This is not very good"

"This is not good. This is not good. This is not very good. This is nice. This very nice. This is not very nice."


person florian    schedule 28.10.2016    source источник
comment
причем здесь n-символ? ifelse(grepl('not|n\'t', x), gsub(sprintf("(?=%s)", paste(targetList, collapse = '|')), "NOT_", x, perl = TRUE), x)   -  person rawr    schedule 28.10.2016
comment
Спасибо @rawr - я отредактировал пост выше, чтобы сделать его более понятным.   -  person florian    schedule 28.10.2016


Ответы (2)


Это должно работать (обновлено с помощью n)

library(stringr)
negation=function(x,n)
{
  target=c("nice", "perfect", "good")
  negate=c("not ","n't")
  out=x
  a=as.data.frame(str_locate(x,negate))
  negate_end=as.numeric(a[!is.na(a$end),]$end)
  b=as.data.frame(str_locate(x,target))
  target_start=as.numeric(b[!is.na(b$start),]$start)
  distance=target_start-negate_end
  distance=ifelse(length(distance)==0,9999999,distance)
  if(sum(!is.na(str_match(x,target)))>0 & distance<=n & distance>=0)
    out=str_replace_all(x,target,paste("NOT_",target,sep=''))[which(!is.na(str_match(x,target)))]
  return(out)
}
person Vishal Jaiswal    schedule 28.10.2016
comment
где вы определяете n? Под n я подразумеваю количество букв, которое товар будет отрицать при наличии отрицания, такого как not или n't. - person florian; 28.10.2016
comment
Извините, Флориан, что пропустил n ранее.. проверьте сейчас, должно работать - person Vishal Jaiswal; 29.10.2016
comment
Спасибо, мы приближаемся, но мы еще не совсем там. Atm, похоже, учитывает отрицания в районе 15 символов, однако он должен учитывать только предшествующие отрицания. Также такие слова, как Nottingham, не должны вызывать его: Тест: negation("This is good. Nottingham is a town.", 15) возвращает: "This is NOT_good. Nottingham is a town." - person florian; 30.10.2016
comment
Спасибо, Флориан. На самом деле это не идентификация не в Ноттингеме, а скорее работа над отрицанием, даже когда не было отрицательных слов. Я исправил сценарий, чтобы позаботиться об этом сейчас. Кроме того, если вы могли бы предоставить мне еще несколько тестовых входных и выходных данных, я могу настроить его (в случае, если обновленный скрипт не работает) - person Vishal Jaiswal; 31.10.2016
comment
Спасибо... хм, теперь я пытаюсь применить его к своему вектору с 500 тысячами текстовых элементов и получаю следующее предупреждение: s_commentsOut$gsubContent <- lapply(s_commentsOut$lowCo, function(x) { negation(x, 15) }) Warning messages: 1: In target_start - negate_end : longer object length is not a multiple of shorter object length - person florian; 01.11.2016
comment
Кажется, это происходит только в нескольких случаях, так как в противном случае алгоритм работает просто весело. - person florian; 01.11.2016
comment
ааа нашел еще одну ошибку..попробуйте со следующей тестовой фразой: negation("This is not good. This is not good. This is not very good. This is nice. This very nice. This is not very nice.", 15) - person florian; 01.11.2016

Вы можете попробовать следующее: (пожалуйста, проверьте, потому что я не уверен на 100%)

require(stringr)
negate <- function(word, phrase, distance_allowed){

  not_pos <- str_locate(tolower(phrase), "^not |not$| not ")

  if (!is.na(not_pos[1])){

      word_pos <- str_locate(tolower(phrase), word)

      if(!is.na(word_pos[1])){

          neg_dist <- ifelse(word_pos[1] > not_pos[1], word_pos[2] - not_pos[1], not_pos[2] - word_pos[1])

        if(neg_dist < distance_allowed ){

             phrase <- gsub(word, paste0("NOT_", word), phrase)

        }


      }

  }
      return(phrase)

}

Моя скромная логика такова:

  1. Найдите не во фразе (она либо начинает фразу, либо находится между словами, либо заканчивает ее, просто чтобы избежать таких слов, как ничего, поскольку я не так хорошо разбираюсь в надоедливых регулярных выражениях)

  2. Если не находится там, найдите позицию слова, если слово найдено, то вычислите расстояние между позицией не и словом (если слово находится перед не, то вычислите расстояние между концом слова и началом не иначе конец не начало слова)

  3. Если это расстояние меньше разрешенного вами (в вашем случае n = 15), выполните изменение

Пожалуйста, проверьте это! Надеюсь, поможет

person User2321    schedule 28.10.2016
comment
Спасибо, к сожалению, я не смог запустить его с помощью моего примитивного теста: pastebin.com/6ExjHLEL - person florian; 29.10.2016