Данная задача родилась из одной практической проблемы.
В Сети можно легко найти ispell-словарь русского языка общей лексики, например здесь. Где используются такие словари, можно прочитать по этой ссылке. Но на тот момент было главное, что ispell-словари являются важной частью полнотекстового поиска в базах данных postgresql. Также важным было то, что в разрабатываемом проекте пользовательский поиск выполнялся c акцентом на географические названия и фамилии. Которых в общем словаре не так уж много.
Поэтому возникла задача поиска (составления, генерации, ...) дополнительных словарей ispell необходимой тематики.
В данной публикации приводится решение этой задачи с попыткой его максимального обобщения.
После экспериментов был выбран следующий подход решения задачи
- Генерация словаря ispell выполняется на основе файла-списка необходимых слов. Получить такой список можно минимум двумя способами: а) составить вручную, б) выделить из словаря языкового корпуса, например, русского или британского
- Каждое слово словаря приводится в нормальную форму. Для этого используются морфологические анализаторы. Для русского языка можно использовать mystem от Yandex или библиотеку python pymorphy2. Для английского языка можно использовать nltk
- Для нормальной формы слова подбирается набор правил образования словоформ на основе составленных правил для ispell-словарей. Скачать эти правила для различных языков можно по этой ссылке
Данный подход реализован в скрипте на python, код приводится в конце публикации. Скрипт реализован для решения задачи со следующими исходными условиями:
- словарь генерируется для русского языка
- на вход скрипту может передаваться словарь Opencorpora или файл с произвольным списком слов
Использование словаря Opencorpora имеет ряд преимуществ:
- на сегодня это самый объемный словарь для русского языка, доступный для свободного скачивания (содержит более 400 тыс. уникальных слов)
- каждая словоформа словаря характеризуется морфологическими признаками (граммеми). Это позволяет выделять для генерируемого словаря только слова нужного типа (например, географические названия или глаголы)
Для приведения слова к нормальной форме в скрипте используется библиотека pymorphy2. Помимо скорости работы и высокого качества распознавания слов, ее преимущество еще и в том, что работа библиотеки основана на словаре Opencorpora и имеет тот же набор граммем.
Вот полный код скрипта
# -*- coding: utf-8 -*-
import codecs
import re
def getFromOpencorpora(filename, grammem):
'''
Function: getWFromOpencorpora
Summary: извлекает из словаря Opencorpora нормальную форму слова
(соответсвующую заданному набору граммем) и ее словоформы
Examples:
Attributes:
@param (filename): путь к имени файла словаря Opencorpora
@param (grammem): массив граммем (признаков) слова, которые необходимо извлекать
из словаря. По сути является фильтром для извлечения слов
нужного типа (глагол, одущивленное сущ. и т.д.)
Returns: Возвращает итератор на генератор пары (список) (исходная форма слова, массив словоформ)
'''
DELIM='[,\s]+'
if type(grammem)==type(''): grammem=[grammem]
f=codecs.open(filename, 'r', 'utf-8')
words_form=[]
ps=re.compile(DELIM)
for line in f:
g=ps.split(line.replace('\n',''))
if len(g)>1:
w=g.pop(0)
if set(grammem)<=set(g) or words_form:
words_form.append(w)
if len(line)<2 and words_form:
n=words_form.pop(0)
ff=set([i.lower() for i in words_form if n!=i])
yield [n.lower(), ff]
words_form=[]
f.close()
def getFromListFile(filename):
'''
Function: getFromListFile
Summary: возвращает каждое слово из файла, хранящего пользовательский список слов
для которого нужно сформировать правила
Examples:
Attributes:
@param (filename): имя файла
Returns: Возвращает итератор на генератор пары (список) (исходная форма слова, массив словоформ)
'''
for f in codecs.open(filename, 'r', 'utf-8'):
yield[f.replace('\n','').lower(),set()]
import pymorphy2
def getFromPymorphy(word, grammem, morph):
'''
Function: getFromPymorphy
Summary: для исходного слова и заданного набора граммем генерирует исходну форму
и возможные словоформы
Examples:
Attributes:
@param (word): исходное слово
@param (grammem): массив нужных граммем для извлекаемого слова и словоформ
@param (morph): объект морфологического анализатора morph = pymorphy2.MorphAnalyzer()
Returns: возвращает список пар - (исходная форма, список словоформ), соответсвующий заданным грамменам
'''
wf=[]
if type(grammem)==type(''): grammem=[grammem]
# исходное слово может распознаться как относящееся к различным типам (по набору граммем)
# например, 'туши'
# на практике такого не должно быть - исходный набор граммем нужно задавать так,
# чтобы однозначно определять категорию слова
for j in morph.parse(word):
f=set()
for i in j.lexeme:
if set(grammem)<=i.tag.grammemes:
f.add(i.word)
if (j.normal_form,f) not in wf:
wf.append((j.normal_form,f))
return wf
def getAffixRules(filename):
r=[]
f=codecs.open(filename, 'r', 'utf-8')
for i in f:
ii=re.split(r'\s+', i[:-1])
if len(ii)>=5:
if ii[2]=='0': ii[2]=''
if ii[3]=='0': ii[3]=''
r.append(ii)
return r
f.close()
if __name__=='__main__':
grammem=['VERB']
f_out_dict=codecs.open("%s.dict" % ('_'.join(grammem).lower(),), 'w', 'utf-8')
rc=[(re.compile(j[4]+'$'), re.compile(j[2]+'$'), j[3], j[1]) for j in getAffixRules('ru.affix')]
morph = pymorphy2.MorphAnalyzer()
for word, wf in getFromOpencorpora("dict.opcorpora.txt", grammem):
#for word, wf in getFromListFile("my_dict.txt"):
for normal_form, words_forms in getFromPymorphy(word, grammem, morph):
r=set()
for p in rc:
if p[0].findall(normal_form):
if p[1].sub(p[2], normal_form) in words_forms:
r.add(p[3])
if r:
f_out_dict.write('%s/%s\n' % (normal_form, ''.join(r)))
f_out_dict.close()
Для работы со словарем Opencorpora используется функция getWFromOpencorpora, для работы с произвольным файлом-списком - getFromListFile.
Для того, чтобы выбрать из Opencorpora только слова нужных типов, необходимо в переменной grammem задать список необходимых граммем. Обозначения граммем можно посмотреть на этой странице
Практическую пользу от данного скрипта (и от этой публикации) можно оценить, ознакомившись с материалами этой статьи
Англоязычные словари ispell
Описанный подход также применяться для генерации словарей ispell для необходимых слов английского языка (и других тоже). Главное здесь - найти подходящий языковой корпус. Например, скрипт для генерации словаря ispell на основе Британского национального корпуса выглядит примерно так
Полезные ссылки