Я спробував PorterStemmer і Snowball, але обидва не працюють над усіма словами, пропускаючи деякі дуже поширені.
Мої тестові слова: " коти, що бігали, спіткали кактуси спільнот кактусів ", і обидва отримують менше половини.
Дивитися також:
Я спробував PorterStemmer і Snowball, але обидва не працюють над усіма словами, пропускаючи деякі дуже поширені.
Мої тестові слова: " коти, що бігали, спіткали кактуси спільнот кактусів ", і обидва отримують менше половини.
Дивитися також:
Відповіді:
Якщо ви знаєте Python, Інструментарій з природних мов (NLTK) має дуже потужний лематизатор, який використовує WordNet .
Зауважте, що якщо ви використовуєте цей лематизатор вперше, ви повинні завантажити корпус перед його використанням. Це можна зробити:
>>> import nltk
>>> nltk.download('wordnet')
Ви повинні зробити це лише один раз. Якщо припустити, що ви зараз завантажили корпус, він працює так:
>>> from nltk.stem.wordnet import WordNetLemmatizer
>>> lmtzr = WordNetLemmatizer()
>>> lmtzr.lemmatize('cars')
'car'
>>> lmtzr.lemmatize('feet')
'foot'
>>> lmtzr.lemmatize('people')
'people'
>>> lmtzr.lemmatize('fantasized','v')
'fantasize'
У модулі nltk.stem є й інші лематизатори , але я сам не пробував їх.
dies
, він дає вам dy
замість цього die
. Хіба не існує якогось словника з твердим кодом
WordNetLemmatizer
неправильно лематизуються?
Я використовую Stanford nlp для проведення лематизації. Я застряг у подібній проблемі останні кілька днів. Все завдяки stackoverflow, який допомагає мені вирішити проблему.
import java.util.*;
import edu.stanford.nlp.pipeline.*;
import edu.stanford.nlp.ling.*;
import edu.stanford.nlp.ling.CoreAnnotations.*;
public class example
{
public static void main(String[] args)
{
Properties props = new Properties();
props.put("annotators", "tokenize, ssplit, pos, lemma");
pipeline = new StanfordCoreNLP(props, false);
String text = /* the string you want */;
Annotation document = pipeline.process(text);
for(CoreMap sentence: document.get(SentencesAnnotation.class))
{
for(CoreLabel token: sentence.get(TokensAnnotation.class))
{
String word = token.get(TextAnnotation.class);
String lemma = token.get(LemmaAnnotation.class);
System.out.println("lemmatized version :" + lemma);
}
}
}
}
Також може бути гарною ідеєю використовувати стоп-слова, щоб мінімізувати вихідні леми, якщо вони будуть використані пізніше в класифікаторі. Погляньте на розширення coreNlp, написане Джоном Конуеллом.
Я спробував ваш список термінів на цьому демо-сайті Snowball, і результати виглядають нормально ....
Стовбур повинен перетворити складні форми слів до якогось загального кореня. Насправді завдання стовбура - зробити цей корінь «правильним» словниковим словом. Для цього вам потрібно переглянути морфологічні / ортографічні аналізатори .
Я вважаю, що це питання стосується більш-менш того самого, і відповідь Каарела на це запитання полягає в тому, звідки я взяв друге посилання.
Дебати про стовбур проти лематизатора тривають. Справа в тому, щоб віддати перевагу точності над ефективністю. Ви повинні лематизуватись для досягнення лінгвістично значущих одиниць і стримуватися, щоб використовувати мінімальний обчислювальний сік і все ж індексувати слово та його варіанти під одним ключем.
Див. " Стеммерс" проти "лематизаторів"
Ось приклад з python NLTK:
>>> sent = "cats running ran cactus cactuses cacti community communities"
>>> from nltk.stem import PorterStemmer, WordNetLemmatizer
>>>
>>> port = PorterStemmer()
>>> " ".join([port.stem(i) for i in sent.split()])
'cat run ran cactu cactus cacti commun commun'
>>>
>>> wnl = WordNetLemmatizer()
>>> " ".join([wnl.lemmatize(i) for i in sent.split()])
'cat running ran cactus cactus cactus community community'
WordNetLemmatizer
's lemmatize()
може взяти POS тег. Отже з вашого прикладу: " ".join([wnl.lemmatize(i, pos=VERB) for i in sent.split()])
дає 'cat run run cactus cactuses cacti community communities'
.
pos=NOUN
? BTW: Давно не
pos=VERB
ви робите лише лематизацію на дієсловах. Іменники залишаються однаковими. Мені просто довелося написати якийсь власний код, щоб обернутися навколо власних тегів POS Penn Treebank POS, щоб застосувати правильну лематизацію до кожного маркера. Також WordNetLemmatizer
смердить на лематизацію токенізатора за замовчуванням nltk. Тому приклади на кшталт does n't
не лематизуються do not
.
port.stem("this")
виробляє thi
і port.stem("was")
wa
навіть тоді, коли для кожного передбачено правильну позицію.
Офіційна сторінка Мартіна Портера містить Портер Стеммер на PHP , а також інших мовах .
Якщо ви дійсно серйозно ставитеся до хорошого виходу, хоча вам потрібно буде почати щось із на зразок алгоритму Портера, уточнити його, додавши правила, щоб виправити неправильні випадки, загальні для вашого набору даних, а потім, нарешті, додати масу винятків із правил . Це можна легко реалізувати за допомогою пар ключів / значень (dbm / хеш / словники), де ключовим є слово, яке слід шукати, а значення - стовбурне слово для заміни оригіналу. Комерційна пошукова система, над якою я працював одного разу, закінчилася 800 винятками з модифікованого алгоритму Портера.
http://wordnet.princeton.edu/man/morph.3WN
Для багатьох моїх проектів я віддаю перевагу лематизатору WordNet на основі лексикону над більш агресивним носієм.
http://wordnet.princeton.edu/links#PHP має посилання на інтерфейс PHP до API WN.
На основі різних відповідей на переповнення стека та блоги, які я натрапив, це метод, який я використовую, і, здається, повертає реальні слова досить добре. Ідея полягає в тому, щоб розділити текст, що вводиться, на масив слів (використовуйте той спосіб, який ви хочете), а потім знайти частини мови (POS) для цих слів і використовувати їх, щоб допомогти визначити і лематизувати слова.
Зразок, який ви знаходитесь вище, не працює надто добре, оскільки POS неможливо визначити. Однак, якщо ми використовуємо реальне речення, справи працюють набагато краще.
import nltk
from nltk.corpus import wordnet
lmtzr = nltk.WordNetLemmatizer().lemmatize
def get_wordnet_pos(treebank_tag):
if treebank_tag.startswith('J'):
return wordnet.ADJ
elif treebank_tag.startswith('V'):
return wordnet.VERB
elif treebank_tag.startswith('N'):
return wordnet.NOUN
elif treebank_tag.startswith('R'):
return wordnet.ADV
else:
return wordnet.NOUN
def normalize_text(text):
word_pos = nltk.pos_tag(nltk.word_tokenize(text))
lemm_words = [lmtzr(sw[0], get_wordnet_pos(sw[1])) for sw in word_pos]
return [x.lower() for x in lemm_words]
print(normalize_text('cats running ran cactus cactuses cacti community communities'))
# ['cat', 'run', 'ran', 'cactus', 'cactuses', 'cacti', 'community', 'community']
print(normalize_text('The cactus ran to the community to see the cats running around cacti between communities.'))
# ['the', 'cactus', 'run', 'to', 'the', 'community', 'to', 'see', 'the', 'cat', 'run', 'around', 'cactus', 'between', 'community', '.']
Перегляньте WordNet, велику лексичну базу даних для англійської мови:
Існують API для доступу до нього на декількох мовах.
Це виглядає цікаво: MIT Java WordnetStemmer: http://projects.csail.mit.edu/jwi/api/edu/mit/jwi/morph/WordnetStemmer.html
Погляньте на LemmaGen - бібліотеку з відкритим кодом, написану на C # 3.0.
Результати для тестових слів ( http://lemmatise.ijs.si/Services )
Пакети верхнього пітона (в довільному порядку) для лематизації є: spacy
, nltk
, gensim
, pattern
, CoreNLP
і TextBlob
. Я віддаю перевагу впровадженню spaCy та gensim (на основі шаблону), оскільки вони ідентифікують POS тег цього слова і призначають відповідну лему автоматично. Дані дають більш відповідні леми, зберігаючи значення недоторканим.
Якщо ви плануєте використовувати nltk або TextBlob, вам потрібно подбати про те, щоб знайти правильний тег POS вручну і знайти правильну лему.
Приклад лематизації за допомогою spaCy:
# Run below statements in terminal once.
pip install spacy
spacy download en
import spacy
# Initialize spacy 'en' model
nlp = spacy.load('en', disable=['parser', 'ner'])
sentence = "The striped bats are hanging on their feet for best"
# Parse
doc = nlp(sentence)
# Extract the lemma
" ".join([token.lemma_ for token in doc])
#> 'the strip bat be hang on -PRON- foot for good'
Приклад лематизації за допомогою Gensim:
from gensim.utils import lemmatize
sentence = "The striped bats were hanging on their feet and ate best fishes"
lemmatized_out = [wd.decode('utf-8').split('/')[0] for wd in lemmatize(sentence)]
#> ['striped', 'bat', 'be', 'hang', 'foot', 'eat', 'best', 'fish']
Наведені вище приклади були запозичені на цій сторінці лематизації .
Здійсніть пошук для Lucene, я не впевнений, що там PHP-порт, але я знаю, що Lucene доступний для багатьох платформ. Lucene - це бібліотека індексування та пошуку OSS (від Apache). Зрозуміло, на це та додаткові громади може бути цікаве поглянути. Принаймні, ви можете дізнатися, як це робиться однією мовою, щоб ви могли перекласти "ідею" на PHP
Якщо я можу процитувати свою відповідь на згадане питання StompChicken:
Основна проблема тут полягає в тому, що алгоритми, що зароджуються, працюють на фонетичній основі, не розуміючи мови, з якою вони працюють.
Оскільки вони не мають розуміння мови і не перебігають зі словника термінів, у них немає способу розпізнавати та відповідати відповідним чином на нерегулярні випадки, такі як "бігти" / "бігати".
Якщо вам потрібно буде розбиратись із нерегулярними випадками, вам доведеться або обрати інший підхід, або доповнити свій вихідний за допомогою власного користувальницького словника виправлень, який слід виконати після того, як стовбек зробив свою справу.
Найновішою версією стовбура в NLTK є Snowball.
Ви можете знайти приклади, як його використовувати тут:
http://nltk.googlecode.com/svn/trunk/doc/api/nltk.stem.snowball2-pysrc.html#demo
Ви можете використати стовбур Morpha. Компанія UW завантажила стефан морфа в Maven central, якщо ви плануєте використовувати його з програми Java. Існує обгортка, яка значно полегшує її використання. Вам просто потрібно додати його як залежність і використовувати edu.washington.cs.knowitall.morpha.MorphaStemmer
клас. Примірники є безпечними для потоків (оригінальний JFlex мав поля класу для локальних змінних без необхідності). Миттєво вкажіть клас та запустіть morpha
і слово, яке ви хочете встановити.
new MorphaStemmer().morpha("climbed") // goes to "climb"
Мартін Портер написав Snowball (мовою для стримування алгоритмів) і переписав "English Stemmer" у Snowball. Є англійський Stemmer для C та Java.
Він прямо зазначає, що штемпер Портера був повторно доповнений лише з історичних причин, тому тестування коректності на кореневій носії отримає результати, про які ви (повинні) вже знаєте.
З http://tartarus.org/~martin/PorterStemmer/index.html (моє наголос)
Стебла Портера слід розцінювати як « заморожений », тобто суворо визначений і не підлягає подальшій модифікації. Що стосується стовбура, він трохи поступається Англійському снігуру або Портер2, що походить від нього, і який зазнає періодичних удосконалень. Для практичної роботи, отже, рекомендується новий стовбур Snowball. Стовбур Портера підходить для ІР-досліджень, пов'язаних із встановленням результатів, де експерименти потрібно точно повторювати.
Доктор Портер пропонує використовувати англійські або Porter2 стовбури замість стомера Портера. Англійська стовбур - це те, що насправді використовується на демонстраційному сайті, як @StompChicken відповів раніше.
На Яві я використовую татаргус-сніжок, щоб переслідувати слова
Maven:
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-snowball</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
Приклад коду:
SnowballProgram stemmer = new EnglishStemmer();
String[] words = new String[]{
"testing",
"skincare",
"eyecare",
"eye",
"worked",
"read"
};
for (String word : words) {
stemmer.setCurrent(word);
stemmer.stem();
//debug
logger.info("Origin: " + word + " > " + stemmer.getCurrent());// result: test, skincar, eyecar, eye, work, read
}
Спробуйте це тут: http://www.twinword.com/lemmatizer.php
Я ввів ваш запит у демонстраційній версії "cats running ran cactus cactuses cacti community communities"
та отримав ["cat", "running", "run", "cactus", "cactus", "cactus", "community", "community"]
з додатковим прапором ALL_TOKENS
.
Зразок коду
Це API, тому ви можете підключитися до нього з будь-якого середовища. Ось як може виглядати дзвінок PHP REST.
// These code snippets use an open-source library. http://unirest.io/php
$response = Unirest\Request::post([ENDPOINT],
array(
"X-Mashape-Key" => [API KEY],
"Content-Type" => "application/x-www-form-urlencoded",
"Accept" => "application/json"
),
array(
"text" => "cats running ran cactus cactuses cacti community communities"
)
);
Я настійно рекомендую використовувати Spacy (синтаксичний аналіз та тегування тексту) та Textacy (обробка тексту вищого рівня, побудована поверх Spacy).
Лематизовані слова за замовчуванням доступні в Spacy як .lemma_
атрибут лексеми, а текст можна лематизувати, роблячи багато інших текстів попередньої обробки текстом. Наприклад, під час створення пакету термінів чи слів або взагалі безпосередньо перед виконанням певної обробки, яка цього вимагає.
Я б радив вам перевірити обидва, перш ніж писати будь-який код, оскільки це може заощадити багато часу!
df_plots = pd.read_excel("Plot Summary.xlsx", index_col = 0)
df_plots
# Printing first sentence of first row and last sentence of last row
nltk.sent_tokenize(df_plots.loc[1].Plot)[0] + nltk.sent_tokenize(df_plots.loc[len(df)].Plot)[-1]
# Calculating length of all plots by words
df_plots["Length"] = df_plots.Plot.apply(lambda x :
len(nltk.word_tokenize(x)))
print("Longest plot is for season"),
print(df_plots.Length.idxmax())
print("Shortest plot is for season"),
print(df_plots.Length.idxmin())
#What is this show about? (What are the top 3 words used , excluding the #stop words, in all the #seasons combined)
word_sample = list(["struggled", "died"])
word_list = nltk.pos_tag(word_sample)
[wnl.lemmatize(str(word_list[index][0]), pos = word_list[index][1][0].lower()) for index in range(len(word_list))]
# Figure out the stop words
stop = (stopwords.words('english'))
# Tokenize all the plots
df_plots["Tokenized"] = df_plots.Plot.apply(lambda x : nltk.word_tokenize(x.lower()))
# Remove the stop words
df_plots["Filtered"] = df_plots.Tokenized.apply(lambda x : (word for word in x if word not in stop))
# Lemmatize each word
wnl = WordNetLemmatizer()
df_plots["POS"] = df_plots.Filtered.apply(lambda x : nltk.pos_tag(list(x)))
# df_plots["POS"] = df_plots.POS.apply(lambda x : ((word[1] = word[1][0] for word in word_list) for word_list in x))
df_plots["Lemmatized"] = df_plots.POS.apply(lambda x : (wnl.lemmatize(x[index][0], pos = str(x[index][1][0]).lower()) for index in range(len(list(x)))))
#Which Season had the highest screenplay of "Jesse" compared to "Walt"
#Screenplay of Jesse =(Occurences of "Jesse")/(Occurences of "Jesse"+ #Occurences of "Walt")
df_plots.groupby("Season").Tokenized.sum()
df_plots["Share"] = df_plots.groupby("Season").Tokenized.sum().apply(lambda x : float(x.count("jesse") * 100)/float(x.count("jesse") + x.count("walter") + x.count("walt")))
print("The highest times Jesse was mentioned compared to Walter/Walt was in season"),
print(df_plots["Share"].idxmax())
#float(df_plots.Tokenized.sum().count('jesse')) * 100 / #float((df_plots.Tokenized.sum().count('jesse') + #df_plots.Tokenized.sum().count('walt') + #df_plots.Tokenized.sum().count('walter')))