ruMorph
Russian Morphology - быстрый морфологический анализатор русского языка на Rust с API для Python
Документация · PyPI · English
ruMorph - морфологический анализатор русского языка: ядро на Rust и API для Python. Для словоформы анализатор находит по словарю OpenCorpora варианты разбора - начальную форму (лемму), часть речи и признаки Universal Dependencies - упорядочивает их по вероятности и строит формы слова.
Разбор и лемматизация примерно в 5 раз быстрее, чем в pymorphy3 с ускорением pymorphy3[fast], форма по признакам и согласование с количеством - в 15-18 раз; списки слов разбираются без GIL. Части речи, признаки и леммы следуют соглашениям трибанка SynTagRus, поэтому результат можно сравнивать с разметкой spaCy, Stanza и других инструментов на UD.
- Анализатор - разборы словоформы по убыванию вероятности, разбор списков слов и лемматизация без GIL, проверка словоформы по словарю
- Части речи и признаки UD - части речи, признаки и леммы в соглашениях SynTagRus; пометы OpenCorpora, у которых нет признака UD
- Токены вне словаря - числа, знаки, латиница, инициалы и сокращения по правилам
- Незнакомые слова - разборы по окончанию, слова через дефис и с приставками, формы угаданных слов
- Словоизменение - формы лексемы, форма по признакам UD, согласование с количеством
- Переход с pymorphy - соответствие вызовов pymorphy2 и pymorphy3, частей речи и граммем
- Скорость и память - разбор, лемматизация и словоизменение в сравнении с pymorphy3, загрузка словаря и память
Установка
Требуется Python 3.11 или новее.
pip install rumorph
Или с помощью uv:
uv add rumorph
Колеса собираются для Linux, macOS и Windows, Rust для установки не нужен; словарь входит в пакет. На других платформах и на Python 3.14t без GIL pip соберет пакет из исходников - для этого нужен Rust 1.88 или новее. Подробнее - на странице Установка.
Быстрый старт
>>> from rumorph import Analyzer
>>> morph = Analyzer()
>>> morph.lemmatize("Мы стали друзьями в ковиде".split())
['мы', 'стать', 'друг', 'в', 'ковид']
>>> [(a.lemma, a.pos, a.feats.get("Case"), round(a.prob, 3)) for a in morph.analyze("стали")][:2]
[('стать', 'VERB', None, 0.975), ('сталь', 'NOUN', 'Gen', 0.011)]
>>> morph.analyze("кошка")[0].inflect(Case="Gen", Number="Plur")
'кошек'
>>> morph.analyze("рубль")[0].agree(5)
'рублей'
Анализатор достаточно создать один раз: при создании он загружает словарь, дальше им можно пользоваться во всей программе.
Возможности
Разбор словоформы
Метод analyze возвращает разборы словоформы по убыванию вероятности - объекты Analysis с леммой, частью речи, признаками UD, пометами словаря, вероятностью и источником разбора. Регистр не важен, буква «е» может обозначать «ё». Разборы неизменяемы: их можно сравнивать, класть в множества и сохранять через pickle.
>>> from rumorph import Analyzer
>>> morph = Analyzer()
>>> morph.analyze("стали")[0]
Analysis(word='стали', lemma='стать', pos='VERB', feats='Aspect=Perf|Mood=Ind|Number=Plur|Tense=Past|VerbForm=Fin|Voice=Mid', labels='intr', prob=0.9753459013836056, source='dictionary')
>>> [len(analyses) for analyses in morph.analyze_many(["мама", "мыла", "раму"])]
[1, 4, 2]
>>> morph.is_known("Ежи"), morph.is_known("вайб")
(True, False)
Списки слов методы analyze_many и lemmatize разбирают без GIL, поэтому несколько потоков с одним анализатором работают параллельно. Анализатор и разборы сохраняются через pickle и работают с multiprocessing и joblib.
Подробнее - в документации: анализатор, разбор словоформы.
Части речи и признаки Universal Dependencies
Части речи и признаки следуют Universal Dependencies в соглашениях крупнейшего русского трибанка UD_Russian-SynTagRus. Отличия от частей речи OpenCorpora:
- «который», «этот», «свой», «весь», «такой» и другие местоименные прилагательные -
DET - «быть» и «бы» -
AUX - вводные слова «однако», «например», «конечно» -
ADV - «можно», «надо», «нельзя» -
VERBбез признаков - предлоги -
ADP, сочинительные союзы -CCONJ, подчинительные -SCONJ, частицы -PART
Признаки: падеж, число, род, одушевленность, лицо, время, вид, наклонение, залог, форма глагола, степень, краткая форма, тип имени собственного, сокращение, притяжательность, тип числительного и его запись, тип местоимения, возвратность, несклоняемость, отрицание. Леммы тоже следуют SynTagRus: «Ивановой» - «иванова», «важнейшей» - «важнейший», «денег» - «деньги». Граммемы OpenCorpora, для которых в UD нет признака, - стиль, переходность, второй предложный падеж и другие - лежат в пометах labels.
>>> from rumorph import Analyzer
>>> morph = Analyzer()
>>> words = ["который", "был", "конечно", "можно", "Ивановой", "денег"]
>>> [(analyses[0].lemma, analyses[0].pos) for analyses in morph.analyze_many(words)]
[('который', 'DET'), ('быть', 'AUX'), ('конечно', 'ADV'), ('можно', 'VERB'), ('иванова', 'PROPN'), ('деньги', 'NOUN')]
>>> lesu = morph.analyze("лесу")[0]
>>> lesu.feats["Case"], lesu.labels
('Loc', frozenset({'loc2'}))
Подробнее - в документации; соответствие граммем OpenCorpora признакам UD - на странице Переход с pymorphy.
Токены вне словаря
Числа, знаки, латиницу и сокращения анализатор разбирает по правилам, source у таких разборов - "rule":
- знаки препинания -
PUNCT, символы%,$,№и эмодзи -SYM - числа цифрами -
NUM; целое число может быть и порядковымADJ, у годов это первый разбор - числа с окончанием «2005-го», «5-ти» - порядковые
ADJс падежом или количественныеNUM, римские числа - порядковыеADJ - слова латиницей -
XсForeign=Yes - инициалы «А.» - имя, отчество или фамилия с
Abbr=Yes - сокращения с точкой «т.», «им.» - разборы словарного сокращения, аббревиатуры капсом «НКО» -
PROPNилиNOUNсAbbr=Yes - наречия «по-бурятски», «по-молодому» -
ADV - прочие токены «км/ч», «РосПил» -
Xбез признаков, лемма - токен в нижнем регистре
Доли неоднозначных разборов взяты из частот в SynTagRus.
>>> from rumorph import Analyzer
>>> morph = Analyzer()
>>> [(a.lemma, a.pos, a.feats["NumType"], round(a.prob, 2)) for a in morph.analyze("2026")]
[('2026', 'ADJ', 'Ord', 0.94), ('2026', 'NUM', 'Card', 0.06)]
>>> [(a.lemma, a.pos, a.feats["Case"]) for a in morph.analyze("2005-го")][:1]
[('2005-й', 'ADJ', 'Gen')]
>>> [(a.pos, a.feats, a.source) for a in morph.analyze("iPhone")]
[('X', {'Foreign': 'Yes'}, 'rule')]
Подробнее - в документации.
Незнакомые слова
Разборы слова кириллицей, которого нет в словаре, анализатор угадывает по окончанию: берет формы словарных слов с тем же окончанием - их часть речи, признаки и словоизменение. source у таких разборов - "guess".
- Слово в нижнем регистре угадывается как нарицательное, слово с заглавной буквы - в основном как имя собственное: фамилия, имя, топоним
- Слово через дефис разбирается по последней части: «крэш-тестов» - «крэш-тест», «Мак-Киннона» - «мак-киннон»
- Слово с известной приставкой - «эко», «анти», «супер», «микро» и другими - разбирается по словарному слову без нее: «экодомов» - «экодом»
- У угаданного разбора есть формы, и они сохраняют заглавные буквы: «Мак-Киннона» в дательном падеже - «Мак-Киннону»
>>> from rumorph import Analyzer
>>> morph = Analyzer()
>>> [(a.lemma, a.pos, a.feats["Case"], a.source) for a in morph.analyze("чатботу")][:1]
[('чатбот', 'NOUN', 'Dat', 'guess')]
>>> guess = morph.analyze("Мартова")[0]
>>> guess.lemma, guess.pos, guess.feats["NameType"], guess.inflect(Case="Dat")
('мартов', 'PROPN', 'Sur', 'Мартову')
>>> morph.analyze("крэш-тестов")[0].lemma, morph.analyze("экодомов")[0].lemma
('крэш-тест', 'экодом')
Подробнее - в документации.
Словоизменение
Каждый разбор знает свою лексему - все формы слова с той же леммой:
forms- формы лексемы в порядке словаряinflect- форма с заданными признаками UD; признаки, которые не названы, по возможности сохраняютсяagree- форма существительного, прилагательного или причастия, согласованная с количеством: «1 рубль», «2 рубля», «5 рублей», «2 суток»
Если у лексемы нет нужной формы, inflect и agree бросают FormNotFoundError, а на опечатку в признаке inflect бросает ValueError со списком допустимых значений.
>>> from rumorph import Analyzer
>>> morph = Analyzer()
>>> cat = morph.analyze("кошка")[0]
>>> [form.word for form in cat.forms()][:5]
['кошка', 'кошки', 'кошке', 'кошку', 'кошкой']
>>> cat.inflect(Case="Gen"), cat.inflect(Case="Gen", Number="Plur")
('кошки', 'кошек')
>>> verb = morph.analyze("читал")[0]
>>> verb.inflect(Gender="Fem"), verb.inflect(Tense="Pres", Person="3", Number="Plur")
('читала', 'читают')
>>> ruble = morph.analyze("рубль")[0]
>>> [f"{n} {ruble.agree(n)}" for n in (1, 2, 5, 21)]
['1 рубль', '2 рубля', '5 рублей', '21 рубль']
Подробнее - в документации.
Переход с pymorphy
ruMorph работает с тем же словарем OpenCorpora, что pymorphy2 и pymorphy3, но API у него свое: части речи и признаки UD вместо граммем OpenCorpora, вероятность вместо score, словоформы строкой, исключения вместо None. Здесь morph - анализатор, p - разбор pymorphy, a - разбор ruMorph:
| pymorphy2, pymorphy3 | ruMorph |
|---|---|
MorphAnalyzer() |
Analyzer() |
morph.parse(word) |
morph.analyze(word) |
[morph.parse(w)[0].normal_form for w in words] |
morph.lemmatize(words) |
morph.word_is_known(word) |
morph.is_known(word) |
p.normal_form, p.tag.POS, p.score |
a.lemma, a.pos, a.prob |
p.tag.case, p.tag.number |
a.feats.get("Case"), a.feats.get("Number") |
p.lexeme |
a.forms() |
p.inflect({"gent", "plur"}).word |
a.inflect(Case="Gen", Number="Plur") |
p.make_agree_with_number(n).word |
a.agree(n) |
Подробнее - в документации: все вызовы, части речи и граммемы.
Скорость и память
Тысяч слов или разборов в секунду, больше - лучше:
| Операция | ruMorph | pymorphy3 | pymorphy3[fast] |
|---|---|---|---|
| Разбор словоформы | 474 | 20 | 105 |
| Лемматизация | 810 | 22 | 170 |
| Лемматизация в 4 потоках | 2 345 | - | - |
| Форма по признакам | 1 400 | 76 | 76 |
| Согласование с количеством | 1 307 | 85 | 85 |
Импорт и загрузка словаря занимают 14 мс против 30 мс у pymorphy3, прирост памяти процесса - 21,5 МБ против 25,5 МБ у pymorphy3[fast]. Замер - на 128 251 словоформе тестовой части UD_Russian-SynTagRus, Apple M3 Pro, Python 3.11.
Подробнее - в документации: все операции и как они измерены.
Разработка
Проект использует uv для управления зависимостями, maturin для сборки модуля на Rust и ruff для линтинга и форматирования. Нужен Rust 1.88 или новее.
git clone https://github.com/SergeyShk/ruMorph.git
cd ruMorph
make deps # создать окружение, установить зависимости и собрать модуль на Rust
make dictionary # собрать словарь из pymorphy3-dicts-ru
make test # запустить тесты Python, примеры из докстрингов (doctest) и тесты Rust
make lint # ruff, mypy, rustfmt и clippy
Полный список команд - make help. Документация двуязычная: русские страницы docs/*.md, английские - docs/*.en.md рядом с ними; при правке страницы обновляйте обе версии.
Перед отправкой изменений стоит установить хуки, которые прогонят линтеры на коммите и тесты на пуше:
uv run pre-commit install
Участие в проекте
Баг-репорты, идеи и пул-реквесты приветствуются - issues открыты. Как устроен процесс и что проверить перед отправкой пул-реквеста - в CONTRIBUTING.md; правила общения - в кодексе поведения.
Структура проекта
- crates/rumorph - ядро анализатора на Rust: формат словаря, сборка, разбор и словоизменение
- crates/rumorph-build - сборка словаря из выгрузки pymorphy3-dicts-ru и перевод тегов OpenCorpora в UD
- crates/rumorph-py - модуль Python
rumorph._rumorphна PyO3 - python/rumorph - пакет Python
- scripts - выгрузка словаря pymorphy3-dicts-ru и проверка установленного пакета перед публикацией
- docs - документация MkDocs на русском и английском
- tests - тесты пакета Python
Авторы
- Шкарин Сергей (kouki.sergey@gmail.com)
Лицензия
Код распространяется под лицензией MIT. Словарь в пакете собран из словаря OpenCorpora и распространяется по лицензии CC BY-SA 3.0 (LICENSE-DICTIONARY.txt).
Цитирование
Если вы используете ruMorph в исследованиях или программах, пожалуйста, цитируйте библиотеку по следующей записи BibTeX - это помогает развитию и поддержке проекта. Те же метаданные лежат в CITATION.cff - GitHub показывает их по кнопке «Cite this repository».
@software{ruMorph,
author = {Sergey Shkarin},
title = {{ruMorph, a fast morphological analyzer for Russian}},
year = 2026,
url = {https://github.com/SergeyShk/ruMorph}
}
Metadata
Release files for rumorph 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| rumorph-0.1.0.tar.gz | 6.5 MB | Without provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| rumorph-0.1.0-cp311-abi3-win_amd64.whl | CPython 3.11 | abi3 | Windows x86-64 | Details |
| rumorph-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| rumorph-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| rumorph-0.1.0-cp311-abi3-macosx_11_0_arm64.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64 | Details |
| rumorph-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl | CPython 3.11 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 39.9 MB
Release files / rumorph-0.1.0.tar.gz
| Download URL | rumorph-0.1.0.tar.gz |
|---|---|
| Size | 6.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6621f1b1609a63862bdf2a2f37b528a131fb59931dd02b6057545ca4621513f
|
|
BLAKE2b-256 checksum How to use checksums |
eadb8d430539babd7914aa4219712272e1e3d88ed477f2050638d7ad5ee59317
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rumorph-0.1.0-cp311-abi3-win_amd64.whl
| Download URL | rumorph-0.1.0-cp311-abi3-win_amd64.whl |
|---|---|
| Size | 6.6 MB |
| Tags | CPython 3.11 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
a9b993577325bc62090e0840be9914149df2686636f979627325d53a2a000274
|
|
BLAKE2b-256 checksum How to use checksums |
c565ab2bafc6d45f869e0026e310f154982eedd5260255c77a95a178a0af5cf5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rumorph-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rumorph-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 6.7 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
78b011101fbf27a28f0cff72fc65b7367584d9cc8e3092cb4b9bc0356f4a5a42
|
|
BLAKE2b-256 checksum How to use checksums |
c1a19f53d9330e972f362a82d1f84bf616c614426dc778f62ad8eb5f59c1e170
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rumorph-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | rumorph-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 6.7 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
46891b29a00a24dc07dfab39b334674e44d729be82783b25534ca449e71f6e04
|
|
BLAKE2b-256 checksum How to use checksums |
8d451ff70d0ab27628e1aab0adb8e084b7ce4f31f29c05a186b19c863338165a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rumorph-0.1.0-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | rumorph-0.1.0-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 6.7 MB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
fc6bf580e2e943de791c574d6cb209e3a22909c681d5903ccc457827397d4725
|
|
BLAKE2b-256 checksum How to use checksums |
f53839c288665607bac90d616c74d3e6255be42e2fd9ded6b99e5d150c5d88b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rumorph-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl
| Download URL | rumorph-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 6.7 MB |
| Tags | CPython 3.11 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
fcaebae876e7f211c9c1ded69d52d73ca16bfa99a7006fc97435dbbc6da4f532
|
|
BLAKE2b-256 checksum How to use checksums |
7a9985d4bd1555a373742c3768625b6ddd0f52c635e8ebe7e87a26abd44b3a6c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for rumorph 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.