Давайте пройдемся по коду и посмотрим, как получить желаемый результат.
Сначала выполняется импорт, у вас есть
import nltk
from nltk import pos_tag
, а затем вы используете
pos_label = nltk.pos_tag(...)
Поскольку вы уже используете from nltk import pos_tag
, pos_tag
уже находится в глобальном пространстве имен, просто выполните:
pos_label = pos_tag(...)
Идиоматически, импорт должен быть немного очищен, чтобы выглядеть примерно так:
from nltk import word_tokenize, pos_tag
from nltk.corpus import wordnet as wn
from nltk.stem import WordNetLemmatizer
wnl = WordNetLemmatizer()
Далее фактически сохранение списка токенизированных слов и затем списка тегов pos, а затем списка лемм по отдельности звучит логично, но, поскольку функция, наконец, только возвращает функцию, вы должны быть в состоянии объединиться pos_tag(word_tokenize(...))
и выполняйте итерацию по ней, чтобы вы могли получить POS-тег и токены, например:
sentence = "I love running angrily"
for word, pos in pos_tag(word_tokenize(sentence)):
print(word, '|', pos)
[out]:
I | PRP
love | VBP
running | VBG
angrily | RB
Теперь мы знаем, что есть несоответствие между выходами pos_tag
и POS, которые ожидает WordNetLemmatizer
. От https://github.com/alvations/pywsd/blob/master/pywsd/utils.py#L124 есть вызов функции penn2morphy
, который выглядит следующим образом:
def penn2morphy(penntag, returnNone=False, default_to_noun=False) -> str:
"""
Converts tags from Penn format (input: single string) to Morphy.
"""
morphy_tag = {'NN':'n', 'JJ':'a', 'VB':'v', 'RB':'r'}
try:
return morphy_tag[penntag[:2]]
except:
if returnNone:
return None
elif default_to_noun:
return 'n'
else:
return ''
Пример:
>>> penn2morphy('JJ')
'a'
>>> penn2morphy('PRP')
''
И если мы используем эти преобразовал теги как входные данные в WordNetLemmatizer
и повторно использовал ваши условия if-else:
sentence = "I love running angrily"
for token, pos in pos_tag(word_tokenize(sentence)):
morphy_pos = penn2morphy(pos)
if morphy_pos in ["a", "n", "v"]:
print(wnl.lemmatize(token, pos=morphy_pos))
elif morphy_pos in ['r']:
print(wn.synset(token+".r.1").lemmas()[0].pertainyms()[0].name())
else:
print(wnl.lemmatize(token))
[out]:
I
love
run
angry
Эй, что ты там делал? Ваш код работает, а мой нет!
Хорошо, теперь мы знаем, как получить желаемый результат. Подведем итоги.
- Сначала мы очищаем импорт
- Затем мы очищаем предварительную обработку (без сохранения промежуточных переменных)
- Затем мы "функционализировали" преобразование POS теги от Penn -> Morphy
- Наконец, мы применили те же условия if / else и запустили лемматизатор.
Но почему мой код не работает? не работает?!
Хорошо, давайте проработаем ваш код, чтобы понять, почему вы получаете ошибку.
Сначала давайте проверим каждый вывод, который вы получаете в функции findTag
, напечатав тип выхода и вывода
sentence = "I love running angrily"
sentence = word_tokenize(sentence)
print(type(sentence))
print(sentence)
[out]:
<class 'list'>
['I', 'love', 'running', 'angrily']
На sentence = word_tokenize(sentence)
вы уже переписали свою исходную переменную в функцию, обычно это признак ошибки позже =)
Теперь давайте посмотрим на следующую строку:
sentence = "I love running angrily"
sentence = word_tokenize(sentence)
sentence = [i.strip(" ") for i in sentence]
print(type(sentence))
print(sentence)
[out]:
<class 'list'>
['I', 'love', 'running', 'angrily']
Теперь мы видим, что sentence = [i.strip(" ") for i in sentence]
фактически бессмысленно, учитывая пример предложения.
В: Но правда ли, что у всех токенов, выводимых word_tokenize
, не будет пробелов / пробелов, которые i.strip(' ')
пытается сделать?
A: Да, похоже, что так. Затем NLTK сначала выполняет операции регулярного выражения над строками, а затем вызывает функцию str.split()
, которая бы убрала пробелы в заголовке / конце между токенами, см. https://github.com/nltk/nltk/blob/develop/nltk/tokenize/destructive.py#L141
Давайте продолжим:
sentence = "I love running angrily"
sentence = word_tokenize(sentence)
sentence = [i.strip(" ") for i in sentence]
pos_label = nltk.pos_tag(sentence)[0][1][0].lower()
print(type(pos_label))
print(pos_label)
[out]:
<class 'str'>
p
Q: Подождите минуту, где pos_label
только одна строка?
Q: А что такое POS-тег p
?
A: Давайте посмотрим ближе, что происходит в nltk.pos_tag(sentence)[0][1][0].lower()
Обычно, когда вам нужно сделать такой [0][1][0]
поиск вложенного индекса, его склонность к ошибкам. Нам нужно спросить, что такое [0][1][0]
?
Мы знаем, что это предложение теперь после того, как sentence = word_tokenize(sentence)
стало списком строк. И pos_tag(sentence)
вернет список кортежей строк, где первый элемент в кортеже является токеном, а второй - тегом POS, то есть
sentence = "I love running angrily"
sentence = word_tokenize(sentence)
sentence = [i.strip(" ") for i in sentence]
thing = pos_tag(sentence)
print(type(thing))
print(thing)
[out]:
<class 'list'>
[('I', 'PRP'), ('love', 'VBP'), ('running', 'VBG'), ('angrily', 'RB')]
Теперь, если мы знаем thing = pos_tag(word_tokenize("I love running angrily"))
, выводит вышеприведенное, давайте поработаем с этим, чтобы увидеть, к чему [0][1][0]
обращается.
>>> thing = [('I', 'PRP'), ('love', 'VBP'), ('running', 'VBG'), ('angrily', 'RB')]
>>> thing[0][1]
('I', 'PRP')
Таким образом, thing[0]
выводит кортеж (token, pos)
для первого токена.
>>> thing = [('I', 'PRP'), ('love', 'VBP'), ('running', 'VBG'), ('angrily', 'RB')]
>>> thing[0][1]
'PRP'
И thing[0][1]
выводит POS для первого токена.
>>> thing = [('I', 'PRP'), ('love', 'VBP'), ('running', 'VBG'), ('angrily', 'RB')]
>>> thing[0][1][0]
'P'
Ахсо, [0][1][0]
ищет первый символ POS первого токена.
Итак, вопрос в том, что желаемое поведение? Если так, то почему вы смотрите только на POS первого слова?
Независимо от того, на что я смотрю. Ваше объяснение все еще не говорит мне, почему TypeError: unhashable type: 'list'
происходит. Прекратите отвлекать меня и скажите мне, как решить TypeError
!!
Хорошо, мы идем дальше, теперь, когда мы знаем thing = pos_tag(word_tokenize("I love running angrily"))
и thing[0][1][0].lower() = 'p'
Учитывая ваши условия if-else,
if pos_label in ["a", "n", "v"]:
print(lem.lemmatize(word, pos = pos_label))
elif pos_label in ['r']:
print(wordnet.synset(sentence+".r.1").lemmas()[0].pertainyms()[0].name())
else:
print(lem.lemmatize(sentence))
мы находим, что значение 'p'
будет перешли к другому, то есть print(lem.lemmatize(sentence))
, но подождите минуту, вспомните, что стало sentence
после того, как вы изменили его с помощью:
>>> sentence = word_tokenize("I love running angrily")
>>> sentence = [i.strip(" ") for i in sentence]
>>> sentence
['I', 'love', 'running', 'angrily']
Так что же произойдет, если мы просто проигнорируем все остальные код и сосредоточиться на этом:
from nltk.stem import WordNetLemmatizer
lem = WordNetLemmatizer()
sentence = ['I', 'love', 'running', 'angrily']
lem.lemmatize(sentence)
[out]:
-------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-34-497ae98ecaa3> in <module>
4 sentence = ['I', 'love', 'running', 'angrily']
5
----> 6 lem.lemmatize(sentence)
~/Library/Python/3.6/lib/python/site-packages/nltk/stem/wordnet.py in lemmatize(self, word, pos)
39
40 def lemmatize(self, word, pos=NOUN):
---> 41 lemmas = wordnet._morphy(word, pos)
42 return min(lemmas, key=len) if lemmas else word
43
~/Library/Python/3.6/lib/python/site-packages/nltk/corpus/reader/wordnet.py in _morphy(self, form, pos, check_exceptions)
1903 # 0. Check the exception lists
1904 if check_exceptions:
-> 1905 if form in exceptions:
1906 return filter_forms([form] + exceptions[form])
1907
TypeError: unhashable type: 'list'
Ах, ха !! Вот где возникает ошибка !!!
Это потому, что WordNetLemmatizer
ожидает ввода одной строки и вы помещаете список строк. Пример использования:
from nltk.stem import WordNetLemmatizer
wnl = WordNetLemmatizer()
token = 'words'
wnl.lemmatize(token, pos='n')
В: Почему вы просто не дошли до сути?!
A: Тогда вы упустите способ отладки вашего код и сделать его лучше =)