Почему подсчет слов nltk отличается от подсчета слов с помощью регулярных выражений? - PullRequest
0 голосов
/ 14 апреля 2020

Вопрос

У нас есть две «версии» одного и того же текста из текстового файла (https://www.gutenberg.org/files/2701/old/moby10b.txt):

  • raw_text = f.read()
  • nltk_text = nltk.Text(nltk.word_tokenize(raw_text))

Чего мне не хватает, так это того, почему nltk_text.vocab()['some_word']) возвращает меньшее число, чем len(re.findall(r'\b(some_word)\b', raw_text))).

Пример полного кода

import nltk
import re

with open('moby.txt', 'r') as f:
    raw_text = f.read()
nltk_text = nltk.Text(nltk.word_tokenize(moby_raw))

print(nltk_text.vocab()['whale'])                    #prints 782
print(len(re.findall(r'\b(whale)\b', raw_text))      #prints 906  

Ответы [ 2 ]

1 голос
/ 14 апреля 2020

Если вы запустите

for word in nltk_text.vocab():
    if 'whale' in word.lower():
        print(word)

, вы увидите длинный список слов, таких как

whale-ship
whale-lance
whale-fishery
right-whale
sperm-whale

, которые не учитываются как whale

Если вы проверите их с регулярным выражением вы видите, что он считает их whale

print(len(re.findall(r'\b(whale)\b', 'whale-hunter whale-lance whale-fishery right-whale sperm-whale'))) 

# prints 5

РЕДАКТИРОВАТЬ:

Используя этот код, я обнаружил несколько ситуаций, когда nltk и regex дает разные результаты

import nltk
import re

with open('Pulpit/moby10b.txt') as f:
    raw_text = f.read()

# --- get all `whale` with few chars around (-4, +10)

word_length = len('whale')
words = []

# search first word at position 0
position = raw_text.find('whale', 0)

while position != -1:
    # get word (with few chars around)
    start = position - 4
    end   = position + word_length + 10
    word  = raw_text[start:end]
    # add word to list
    words.append(word)
    # search next word at position `position+1`
    position = raw_text.find('whale', position+1)

# --- test words with nltk and regex

for word in words:

    nltk_text = nltk.Text(nltk.word_tokenize(word))
    number_1 = nltk_text.vocab()['whale']
    number_2 = len(re.findall(r'\b(?<!-)(whale)(?!-)\b', word))
    if number_1 != number_2:
        print(number_1, number_2, word)
        print('-----')

Результат:

1 0 ite whale--did ye m
-----
1 0 ite whale--shirr! s
-----
1 0 erm
whale--squid or
-----
0 1 erm whale's
head em
-----
0 1 the whale's
Decapit
-----
0 1 the whale's
headlon
-----
0 1 the whale's
eyes ha
-----
1 0 EAD whale--even as 
-----
0 1 the whale's
flukes 
-----
1 0 one whale--as a sol
-----
0 1 the whale's
vocabul
-----
1 0 rst
whale--a boy-ha
-----
1 0 the whale--modifyin
-----

Я показываю две ситуации

  1. whale-- с двойным -

    nltk считает, но regex не считает.

  2. whale's\nhead с \n между whale's и следующим заголовком слова

    nltk не учитывает его (но вместо этого он считается, когда есть место \n или когда есть место после / до \n), но regex учитывает его в каждой ситуации.

0 голосов
/ 14 апреля 2020

Основная причина, по которой это происходит, заключается в токенизации . Токен - это не всегда слово, это концепция НЛП, в которую автор не будет вдаваться сейчас. Если вам нужно точное совпадение для слова, а не для токена, используйте wordpunct_tokenize вместо word_tokenize . Пример кода ниже.

nltk_text = nltk.Text(nltk.word_tokenize(raw_text))
nltk_text2 = nltk.Text(nltk.wordpunct_tokenize(raw_text))
print(nltk_text.vocab()['whale']) #782
print(nltk_text2.vocab()['whale']) #906
print(len(re.findall(r'whale', raw_text))) #906

Предлагаемое дальнейшее чтение здесь

...