Кто подскажет JS-либу для выделения русских корней слов?
Для подсчёта числа одинаковых слов без учёта падежей и склонений нужна библиотека, которая знает правила склонений и, возможно, ряд исключений частоупотребительных слов (типа «лёд-льда», «идёт-шёл-шли»). Из неё можно было бы сделать счётчик частоты слов в статье и прикрутить на Хабр, что будет лучше показывать направленность статьи, чем теги и хабы, выбранные автором. В общем, ориентация её такая: пусть не очень точно работает (всё равно ошибки в статье сводят точность на нет), но создаёт представление о частых словах. (Затем удалим общую лексику, но это детали — нужен движок).
Не то, что хотелось (мало языковых знаний, нет изменяемых корней и префиксов), но, видимо, придётся руками собирать по частям, чтобы было нечто интересное.
Вот все окончания, для справки пусть тут полежат:
Скрытый текст
//Snowball JavaScript Library v0.3
var a_0 = [
new Among("в", -1, 1),
new Among("ив", 0, 2),
new Among("ыв", 0, 2),
new Among("вши", -1, 1),
new Among("ивши", 3, 2),
new Among("ывши", 3, 2),
new Among("вшись", -1, 1),
new Among("ившись", 6, 2),
new Among("ывшись", 6, 2)
],
a_1 = [
new Among("ее", -1, 1),
new Among("ие", -1, 1),
new Among("ое", -1, 1),
new Among("ые", -1, 1),
new Among("ими", -1, 1),
new Among("ыми", -1, 1),
new Among("ей", -1, 1),
new Among("ий", -1, 1),
new Among("ой", -1, 1),
new Among("ый", -1, 1),
new Among("ем", -1, 1),
new Among("им", -1, 1),
new Among("ом", -1, 1),
new Among("ым", -1, 1),
new Among("его", -1, 1),
new Among("ого", -1, 1),
new Among("ему", -1, 1),
new Among("ому", -1, 1),
new Among("их", -1, 1),
new Among("ых", -1, 1),
new Among("ею", -1, 1),
new Among("ою", -1, 1),
new Among("ую", -1, 1),
new Among("юю", -1, 1),
new Among("ая", -1, 1),
new Among("яя", -1, 1)
],
a_2 = [
new Among("ем", -1, 1),
new Among("нн", -1, 1),
new Among("вш", -1, 1),
new Among("ивш", 2, 2),
new Among("ывш", 2, 2),
new Among("щ", -1, 1),
new Among("ющ", 5, 1),
new Among("ующ", 6, 2)
],
a_3 = [
new Among("сь", -1, 1),
new Among("ся", -1, 1)
],
a_4 = [
new Among("ла", -1, 1),
new Among("ила", 0, 2),
new Among("ыла", 0, 2),
new Among("на", -1, 1),
new Among("ена", 3, 2),
new Among("ете", -1, 1),
new Among("ите", -1, 2),
new Among("йте", -1, 1),
new Among("ейте", 7, 2),
new Among("уйте", 7, 2),
new Among("ли", -1, 1),
new Among("или", 10, 2),
new Among("ыли", 10, 2),
new Among("й", -1, 1),
new Among("ей", 13, 2),
new Among("уй", 13, 2),
new Among("л", -1, 1),
new Among("ил", 16, 2),
new Among("ыл", 16, 2),
new Among("ем", -1, 1),
new Among("им", -1, 2),
new Among("ым", -1, 2),
new Among("н", -1, 1),
new Among("ен", 22, 2),
new Among("ло", -1, 1),
new Among("ило", 24, 2),
new Among("ыло", 24, 2),
new Among("но", -1, 1),
new Among("ено", 27, 2),
new Among("нно", 27, 1),
new Among("ет", -1, 1),
new Among("ует", 30, 2),
new Among("ит", -1, 2),
new Among("ыт", -1, 2),
new Among("ют", -1, 1),
new Among("уют", 34, 2),
new Among("ят", -1, 2),
new Among("ны", -1, 1),
new Among("ены", 37, 2),
new Among("ть", -1, 1),
new Among("ить", 39, 2),
new Among("ыть", 39, 2),
new Among("ешь", -1, 1),
new Among("ишь", -1, 2),
new Among("ю", -1, 2),
new Among("ую", 44, 2)
],
a_5 = [
new Among("а", -1, 1),
new Among("ев", -1, 1),
new Among("ов", -1, 1),
new Among("е", -1, 1),
new Among("ие", 3, 1),
new Among("ье", 3, 1),
new Among("и", -1, 1),
new Among("еи", 6, 1),
new Among("ии", 6, 1),
new Among("ами", 6, 1),
new Among("ями", 6, 1),
new Among("иями", 10, 1),
new Among("й", -1, 1),
new Among("ей", 12, 1),
new Among("ией", 13, 1),
new Among("ий", 12, 1),
new Among("ой", 12, 1),
new Among("ам", -1, 1),
new Among("ем", -1, 1),
new Among("ием", 18, 1),
new Among("ом", -1, 1),
new Among("ям", -1, 1),
new Among("иям", 21, 1),
new Among("о", -1, 1),
new Among("у", -1, 1),
new Among("ах", -1, 1),
new Among("ях", -1, 1),
new Among("иях", 26, 1),
new Among("ы", -1, 1),
new Among("ь", -1, 1),
new Among("ю", -1, 1),
new Among("ию", 30, 1),
new Among("ью", 30, 1),
new Among("я", -1, 1),
new Among("ия", 33, 1),
new Among("ья", 33, 1)
],
a_6 = [
new Among("ост", -1, 1),
new Among("ость", -1, 1)
],
a_7 = [
new Among("ейше", -1, 1),
new Among("н", -1, 2),
new Among("ейш", -1, 1),
new Among("ь", -1, 3)
]
Словарь тыс на 20 слов вполне можно поместить в браузер, только работать с ним древовидно. В такой объём поместятся все исключения и несколько спецсловарей сверху.
Тогда можно наверное как-то так:
исключения -> основа слова, или слово для стемминга (с исп. словаря, «лёд -> льда» или «лёд -> льд»)
потом
стемминг слов -> основа слова («льду», «льда» -> «льд»),
а затем
полученные основы слов -> эталон слова (с исп. словаря, «льд -> лёд, коню -> конь»)
Никогда такого не проворачивал. Если возьмётесь и найдёте словари, напишите мне о результатах, пожалуйста.
В смысле, потом объединить известные исключения? В общем, да, тогда полдела будет сделано. Но неучёт приставок — это значит, что в исключения попадут почти все слова. Возможно, тут надо на полученном списке провести обратный стемминг по приставкам (чтобы работала общая процедура, только окончания заменить на приставки и инвертировать список), из оставшегося списка — работа с исключениями.
Да, про приставки я забыл. Но для выделения «направленности статьи» вместо тегов и хабов лучше создать свой словарь с готовыми словами-маркерами и шлюхами, наподобие:
Это будет гораздо лучше, чем пытаться отфильтровать контент по, например, таким словам ['и', 'а', 'плюсую', 'карма', '*дрочер', 'работа'] и прочим, честное слово.