You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

natasha

Package Overview
Dependencies
Maintainers
2
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

natasha - pypi Package Compare versions

Comparing version
1.4.0
to
1.5.0
+26
.github/workflows/pypi.yml
name: Publish PyPi
on:
push:
tags:
- v*
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install wheel
- name: Build package
run: python setup.py sdist bdist_wheel
- name: Publish PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
name: Test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r requirements/ci.txt
pip install -e .
- name: Test
run: make test
Dima Veselov (@dveselov)
Danil Yarancev (@TiberiumPY)
Ilya Baryshnikov (@Lucidyan)
Alexander Kukushkin (@kuk)
Artur Bekerov (@bekerov)
The MIT License (MIT)
Copyright (c) 2016
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
ipykernel
nbconvert
pytest
flake8
import pytest
from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
NamesExtractor,
DatesExtractor,
MoneyExtractor,
AddrExtractor,
)
@pytest.fixture(scope='session')
def segmenter():
return Segmenter()
@pytest.fixture(scope='session')
def morph_vocab():
return MorphVocab()
@pytest.fixture(scope='session')
def embedding():
return NewsEmbedding()
@pytest.fixture(scope='session')
def morph_tagger(embedding):
return NewsMorphTagger(embedding)
@pytest.fixture(scope='session')
def syntax_parser(embedding):
return NewsSyntaxParser(embedding)
@pytest.fixture(scope='session')
def ner_tagger(embedding):
return NewsNERTagger(embedding)
@pytest.fixture(scope='session')
def names_extractor(morph_vocab):
return NamesExtractor(morph_vocab)
@pytest.fixture(scope='session')
def dates_extractor(morph_vocab):
return DatesExtractor(morph_vocab)
@pytest.fixture(scope='session')
def money_extractor(morph_vocab):
return MoneyExtractor(morph_vocab)
@pytest.fixture(scope='session')
def addr_extractor(morph_vocab):
return AddrExtractor(morph_vocab)
import pytest
from natasha.obj import (
AddrPart as Part,
Addr
)
tests = [
[
'Россия, Вологодская обл. г. Череповец, пр.Победы 93 б',
Addr([
Part('Россия', 'страна'),
Part('Вологодская', 'область'),
Part('Череповец', 'город'),
Part('Победы', 'проспект'),
])
],
[
'692909, РФ, Приморский край, г. Находка, ул. Добролюбова, 18',
Addr([
Part('692909', 'индекс'),
Part('РФ', 'страна'),
Part('Приморский', 'край'),
Part('Находка', 'город'),
Part('Добролюбова', 'улица'),
])
],
[
'д. Федоровка, ул. Дружбы, 13',
Addr([
Part('Федоровка', 'деревня'),
Part('Дружбы', 'улица'),
])
],
[
'Россия, 129110, г.Москва, Олимпийский проспект, 22',
Addr([
Part('Россия', 'страна'),
Part('129110', 'индекс'),
Part('Москва', 'город'),
Part('Олимпийский', 'проспект'),
])
],
[
'г. Санкт-Петербург, Красногвардейский пер., д. 15',
Addr([
Part('Санкт-Петербург', 'город'),
Part('Красногвардейский', 'переулок'),
Part('15', 'дом')
])
],
[
'Республика Карелия,г.Петрозаводск,ул.Маршала Мерецкова, д.8 Б,офис 4',
Addr([
Part('Карелия', 'республика'),
Part('Петрозаводск', 'город'),
Part('Маршала Мерецкова', 'улица'),
Part('8 Б', 'дом'),
Part('4', 'офис')
])
],
[
'628000, ХМАО-Югра, г.Ханты-Мансийск, ул. Ледовая , д.19',
Addr([
Part('628000', 'индекс'),
Part('ХМАО-Югра'),
Part('Ханты-Мансийск', 'город'),
Part('Ледовая', 'улица'),
Part('19', 'дом')
])
],
[
'ХМАО г.Нижневартовск пер.Ягельный 17',
Addr([
Part('ХМАО'),
Part('Нижневартовск', 'город'),
Part('Ягельный', 'переулок'),
])
],
[
'Белгородская обл, пгт Борисовка,ул. Рудого д.160',
Addr([
Part('Белгородская', 'область'),
Part('Борисовка', 'посёлок'),
Part('Рудого', 'улица'),
Part('160', 'дом')
])
],
[
'Самарская область, п.г.т. Алексеевка, ул. Ульяновская д. 21',
Addr([
Part('Самарская', 'область'),
Part('Алексеевка', 'посёлок'),
Part('Ульяновская', 'улица'),
Part('21', 'дом')
])
],
[
'Мурманская обл поселок городского типа Молочный, ул.Гальченко д.11',
Addr([
Part('Мурманская', 'область'),
Part('Молочный', 'посёлок'),
Part('Гальченко', 'улица'),
Part('11', 'дом')
])
],
[
'ул. Народного Ополчения д. 9к.3',
Addr([
Part('Народного Ополчения', 'улица'),
Part('9к', 'дом'),
])
],
[
'ул. Б. Пироговская, д.37/430',
Addr([
Part('Б. Пироговская', 'улица'),
Part('37/430', 'дом')
])
],
[
'Ставропольский край, Изобильненский район, город Изобильный, улица Чапаева, дом 68',
Addr([
Part('Ставропольский', 'край'),
Part('Изобильненский', 'район'),
Part('Изобильный', 'город'),
Part('Чапаева', 'улица'),
Part('68', 'дом')
])
],
]
@pytest.mark.parametrize('test', tests)
def test_extractor(addr_extractor, test):
text, target = test
pred = addr_extractor.find(text).fact
assert pred == target
import pytest
from natasha.obj import Date
tests = [
[
'24.01.2017',
Date(2017, 1, 24)
],
[
'27. 05.99',
Date(1999, 5, 27)
],
[
'2015 год',
Date(2015)
],
[
'2014 г',
Date(2014)
],
[
'1 апреля',
Date(None, 4, 1)
],
[
'май 2017 г.',
Date(2017, 5)
],
[
'9 мая 2017 года',
Date(2017, 5, 9)
],
]
@pytest.mark.parametrize('test', tests)
def test_extractor(dates_extractor, test):
text, target = test
pred = dates_extractor.find(text).fact
assert pred == target
import re
from natasha import PER, Doc
TEXT = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года).'
def strip(markup):
markup = markup.lstrip('\n')
return re.sub(r'\s+\n', '\n', markup)
NER = strip('''
Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав
LOC──── LOC──── PER───────
о решении властей Львовской области объявить 2019 год годом лидера
LOC──────────────
запрещенной в России Организации украинских националистов (ОУН)
LOC─── ORG───────────────────────────────────────
Степана Бандеры. Свое заявление он разместил в Twitter. 11 декабря
PER──────────── ORG────
Львовский областной совет принял решение провозгласить 2019 год в
ORG──────────────────────
регионе годом Степана Бандеры в связи с празднованием 110-летия со дня
PER────────────
рождения лидера ОУН (Бандера родился 1 января 1909 года).
ORG
''')
MORPH = strip('''
Посол NOUN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Израиля PROPN|Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing
на ADP
Украине PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Йоэль PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Лион PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
признался VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Mid
, PUNCT
что SCONJ
пришел VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Act
в ADP
шок NOUN|Animacy=Inan|Case=Acc|Gender=Masc|Number=Sing
, PUNCT
узнав VERB|Aspect=Perf|Tense=Past|VerbForm=Conv|Voice=Act
о ADP
решении NOUN|Animacy=Inan|Case=Loc|Gender=Neut|Number=Sing
властей NOUN|Animacy=Inan|Case=Gen|Gender=Fem|Number=Plur
Львовской ADJ|Case=Gen|Degree=Pos|Gender=Fem|Number=Sing
области NOUN|Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing
объявить VERB|Aspect=Perf|VerbForm=Inf|Voice=Act
2019 ADJ
год NOUN|Animacy=Inan|Case=Acc|Gender=Masc|Number=Sing
годом NOUN|Animacy=Inan|Case=Ins|Gender=Masc|Number=Sing
лидера NOUN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Sing
запрещенной VERB|Aspect=Perf|Case=Gen|Gender=Fem|Number=Sing|Tense=Past|VerbForm=Part|Voice=Pass
в ADP
России PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Организации PROPN|Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing
украинских ADJ|Case=Gen|Degree=Pos|Number=Plur
националистов NOUN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Plur
( PUNCT
ОУН PROPN|Animacy=Inan|Case=Nom|Gender=Fem|Number=Sing
) PUNCT
Степана PROPN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Sing
Бандеры PROPN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Sing
. PUNCT
''')
SYNTAX = strip('''
┌──► Посол nsubj
│ Израиля
│ ┌► на case
│ └─ Украине
│ ┌─ Йоэль
│ └► Лион flat:name
┌─────┌─└─── признался
│ │ ┌──► , punct
│ │ │ ┌► что mark
│ └►└─└─ пришел ccomp
│ │ ┌► в case
│ └──►└─ шок obl
│ ┌► , punct
│ ┌────►┌─└─ узнав advcl
│ │ │ ┌► о case
│ │ ┌───└►└─ решении obl
│ │ │ ┌─└──► властей nmod
│ │ │ │ ┌► Львовской amod
│ │ │ └──►└─ области nmod
│ └─└►┌─┌─── объявить nmod
│ │ │ ┌► 2019 amod
│ │ └►└─ год obj
│ └──►┌─ годом obl
│ ┌─────└► лидера nmod
│ │ ┌►┌─── запрещенной acl
│ │ │ │ ┌► в case
│ │ │ └►└─ России obl
│ ┌─└►└─┌─── Организации nmod
│ │ │ ┌► украинских amod
│ │ ┌─└►└─ националистов nmod
│ │ │ ┌► ( punct
│ │ └►┌─└─ ОУН parataxis
│ │ └──► ) punct
│ └──────►┌─ Степана appos
│ └► Бандеры flat:name
└──────────► . punct
''')
LEMMAS = {
'110-летия': '110-летие',
'Бандеры': 'бандера',
'Израиля': 'израиль',
'Львовской': 'львовский',
'Организации': 'организация',
'России': 'россия',
'Свое': 'свой',
'Степана': 'степан',
'Украине': 'украина',
'властей': 'власть',
'года': 'год',
'годом': 'год',
'декабря': 'декабрь',
'дня': 'день',
'запрещенной': 'запретить',
'лидера': 'лидер',
'националистов': 'националист',
'области': 'область',
'празднованием': 'празднование',
'признался': 'признаться',
'принял': 'принять',
'пришел': 'прийти',
'разместил': 'разместить',
'регионе': 'регион',
'решении': 'решение',
'родился': 'родиться',
'рождения': 'рождение',
'связи': 'связь',
'со': 'с',
'узнав': 'узнать',
'украинских': 'украинский',
'января': 'январь'
}
NORMALS = {
'Twitter': 'Twitter',
'Израиля': 'Израиль',
'Йоэль Лион': 'Йоэль Лион',
'Львовский областной совет': 'Львовский областной совет',
'Львовской области': 'Львовская область',
'ОУН': 'ОУН',
'Организации украинских националистов (ОУН)': 'Организация украинских '
'националистов (ОУН)',
'России': 'Россия',
'Степана Бандеры': 'Степан Бандера',
'Украине': 'Украина'
}
FACTS = {
'Йоэль Лион': {'first': 'Йоэль', 'last': 'Лион'},
'Степан Бандера': {'first': 'Степан', 'last': 'Бандера'}
}
def test_doc(segmenter, morph_vocab,
morph_tagger, syntax_parser, ner_tagger,
names_extractor, capsys):
doc = Doc(TEXT)
doc.segment(segmenter)
doc.tag_morph(morph_tagger)
doc.parse_syntax(syntax_parser)
doc.tag_ner(ner_tagger)
for span in doc.spans:
span.normalize(morph_vocab)
if span.type == PER:
span.extract_fact(names_extractor)
for token in doc.tokens:
token.lemmatize(morph_vocab)
doc.ner.print()
assert strip(capsys.readouterr().out) == NER
sent = doc.sents[0]
sent.morph.print()
assert strip(capsys.readouterr().out) == MORPH
sent.syntax.print()
assert strip(capsys.readouterr().out) == SYNTAX
lemmas = {
_.text: _.lemma
for _ in doc.tokens
if _.text.lower() != _.lemma
}
assert lemmas == LEMMAS
normals = {
_.text: _.normal
for _ in doc.spans
}
assert normals == NORMALS
facts = {
_.normal: _.fact.as_dict
for _ in doc.spans
if _.fact
}
assert facts == FACTS
import pytest
tests = [
[
'1 599 059, 38 Евро',
'1599059.38 EUR'
],
[
'2 134 472,44 рубля',
'2134472.44 RUB'
],
[
'420 долларов',
'420.00 USD'
],
[
'20 млн руб',
'20000000.00 RUB'],
[
'20 000 долларов',
'20000.00 USD'
],
[
'2,2 млн.руб.',
'2200000.00 RUB'
],
[
'2,20 млн.руб.',
'2200000.00 RUB'
],
[
'2,02 млн.руб.',
'2020000.00 RUB'
],
[
'20 тыс руб',
'20000.00 RUB'
],
[
'20 т. р.',
'20000.00 RUB'
],
[
'2 200 000 руб.',
'2200000.00 RUB'
],
[
'20.000 руб.',
'20000.00 RUB'
],
[
'20,000 руб',
'20000.00 RUB'
],
[
'20,00 руб',
'20.00 RUB'
],
[
'124 451 рубль 50 копеек',
'124451.50 RUB',
],
[
'881 913 (Восемьсот восемьдесят одна тысяча девятьсот тринадцать) руб. 98 коп.',
'881913.98 RUB'
]
]
@pytest.mark.parametrize('test', tests)
def test_extractor(money_extractor, test):
text, target = test
fact = money_extractor.find(text).fact
pred = '{fact.amount:0.2f} {fact.currency}'.format(fact=fact)
assert pred == target
import pytest
from natasha.obj import Name
tests = [
[
'Мустафа Джемилев',
Name('Мустафа', 'Джемилев')
],
[
'Егор Свиридов',
Name('Егор', 'Свиридов')
],
[
'Владимир Путин',
Name('Владимир', 'Путин')
],
[
'Плаксюк Саша',
Name('Саша', 'Плаксюк')
],
[
'Ищенко Е.П.',
Name('Е', 'Ищенко', 'П')
],
[
'Фёдор Иванович Шаляпин',
Name('Фёдор', 'Шаляпин', 'Иванович')
],
[
'Ипполит Матвеевич',
Name('Ипполит', 'Матвеевич')
],
[
'Янукович',
Name(None, 'Янукович')
],
[
'Авраам',
Name('Авраам')
],
[
'Гоша Куценко',
Name('Гоша', 'Куценко')
],
[
'Юрий Георгиевич Куценко',
Name('Юрий', 'Куценко', 'Георгиевич')
],
[
'Наталья Ищенко',
Name('Наталья', 'Ищенко')
],
[
'Наталья Сергеевна Ищенко',
Name('Наталья', 'Ищенко', 'Сергеевна')
],
[
'Монина Нина Гафуровна',
Name('Нина', 'Монина', 'Гафуровна')
],
[
'В. И. Ленин',
Name('В', 'Ленин', 'И'),
],
]
@pytest.mark.parametrize('test', tests)
def test_extractor(names_extractor, test):
text, target = test
pred = names_extractor.find(text).fact
assert pred == target
import pytest
from natasha.span import (
Span,
envelop_spans
)
tests = [
[
[(0, 1)],
[],
[]
],
[
[],
[(0, 1)],
[[]]
],
[
[(0, 1), (1, 2)],
[(1, 2)],
[[(1, 2)]]
],
[
[(0, 1), (1, 2)],
[(0, 1)],
[[(0, 1)]]
],
[
[(0, 1), (1, 2)],
[(0, 1), (1, 2)],
[[(0, 1)], [(1, 2)]]
],
]
def adapt_spans(spans):
for start, stop in spans:
yield Span(start, stop, type=None)
@pytest.mark.parametrize('test', tests)
def test_envelope_spans(test):
spans, envelopes, target = test
spans = list(adapt_spans(spans))
envelopes = list(adapt_spans(envelopes))
target = [
list(adapt_spans(group))
for group in target
]
pred = list(envelop_spans(spans, envelopes))
assert pred == target
+372
-355
Metadata-Version: 2.1
Name: natasha
Version: 1.4.0
Version: 1.5.0
Summary: Named-entity recognition for russian language

@@ -9,357 +9,3 @@ Home-page: https://github.com/natasha/natasha

License: MIT
Description:
<img src="https://github.com/natasha/natasha-logos/blob/master/natasha.svg">
![CI](https://github.com/natasha/natasha/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/natasha/natasha/branch/master/graph/badge.svg)](https://codecov.io/gh/natasha/natasha)
Natasha solves basic NLP tasks for Russian language: tokenization, sentence segmentation, word embedding, morphology tagging, lemmatization, phrase normalization, syntax parsing, NER tagging, fact extraction. Quality on every task is similar or better then current SOTAs for Russian language on news articles, see <a href="https://github.com/natasha/natasha#evaluation">evaluation section</a>. Natasha is not a research project, underlying technologies are built for production. We pay attention to model size, RAM usage and performance. Models run on CPU, use Numpy for inference.
Natasha integrates libraries from <a href="https://github.com/natasha">Natasha project</a> under one convenient API:
* <a href="https://github.com/natasha/razdel">Razdel</a> — token, sentence segmentation for Russian
* <a href="https://github.com/natasha/navec">Navec</a> — compact Russian embeddings
* <a href="https://github.com/natasha/slovnet">Slovnet</a> — modern deep-learning techniques for Russian NLP, compact models for Russian morphology, syntax, NER.
* <a href="https://github.com/natasha/yargy">Yargy</a> — rule-based fact extraction similar to Tomita parser.
* <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> — NLP visualizations for NER and syntax markups.
> ⚠ API may change, for realworld tasks consider using low level libraries from Natasha project.
> Models optimized for news articles, quality on other domain may be lower.
> To use old `NamesExtractor`, `AddressExtactor` downgrade `pip install natasha<1 yargy<0.13`
## Install
Natasha supports Python 3.5+ and PyPy3:
```bash
$ pip install natasha
```
## Usage
For more examples and explanation see [Natasha documentation](http://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb).
```python
>>> from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
PER,
NamesExtractor,
Doc
)
#######
#
# INIT
#
#####
>>> segmenter = Segmenter()
>>> morph_vocab = MorphVocab()
>>> emb = NewsEmbedding()
>>> morph_tagger = NewsMorphTagger(emb)
>>> syntax_parser = NewsSyntaxParser(emb)
>>> ner_tagger = NewsNERTagger(emb)
>>> names_extractor = NamesExtractor(morph_vocab)
>>> text = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу понять, как прославление тех, кто непосредственно принимал участие в ужасных антисемитских преступлениях, помогает бороться с антисемитизмом и ксенофобией. Украина не должна забывать о преступлениях, совершенных против украинских евреев, и никоим образом не отмечать их через почитание их исполнителей», — написал дипломат. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В июле аналогичное решение принял Житомирский областной совет. В начале месяца с предложением к президенту страны Петру Порошенко вернуть Бандере звание Героя Украины обратились депутаты Верховной Рады. Парламентарии уверены, что признание Бандеры национальным героем поможет в борьбе с подрывной деятельностью против Украины в информационном поле, а также остановит «распространение мифов, созданных российской пропагандой». Степан Бандера (1909-1959) был одним из лидеров Организации украинских националистов, выступающей за создание независимого государства на территориях с украиноязычным населением. В 2010 году в период президентства Виктора Ющенко Бандера был посмертно признан Героем Украины, однако впоследствии это решение было отменено судом. '
>>> doc = Doc(text)
#######
#
# SEGMENT
#
#####
>>> doc.segment(segmenter)
>>> display(doc.tokens[:5])
>>> display(doc.sents[:5])
[DocToken(stop=5, text='Посол'),
DocToken(start=6, stop=13, text='Израиля'),
DocToken(start=14, stop=16, text='на'),
DocToken(start=17, stop=24, text='Украине'),
DocToken(start=25, stop=30, text='Йоэль')]
[DocSent(stop=218, text='Посол Израиля на Украине Йоэль Лион признался, чт..., tokens=[...]),
DocSent(start=219, stop=257, text='Свое заявление он разместил в Twitter.', tokens=[...]),
DocSent(start=258, stop=424, text='«Я не могу понять, как прославление тех, кто непо..., tokens=[...]),
DocSent(start=425, stop=592, text='Украина не должна забывать о преступлениях, совер..., tokens=[...]),
DocSent(start=593, stop=798, text='11 декабря Львовский областной совет принял решен..., tokens=[...])]
#######
#
# MORPH
#
#####
>>> doc.tag_morph(morph_tagger)
>>> display(doc.tokens[:5])
>>> doc.sents[0].morph.print()
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
Посол NOUN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Израиля PROPN|Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing
на ADP
Украине PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Йоэль PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Лион PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
признался VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Mid
, PUNCT
что SCONJ
...
######
#
# LEMMA
#
#######
>>> for token in doc.tokens:
>>> token.lemmatize(morph_vocab)
>>> display(doc.tokens[:5])
>>> {_.text: _.lemma for _ in doc.tokens}
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>, lemma='посол'),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>, lemma='израиль'),
DocToken(start=14, stop=16, text='на', pos='ADP', lemma='на'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>, lemma='украина'),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>, lemma='йоэль')]
{'Посол': 'посол',
'Израиля': 'израиль',
'на': 'на',
'Украине': 'украина',
'Йоэль': 'йоэль',
'Лион': 'лион',
'признался': 'признаться',
',': ',',
'что': 'что',
'пришел': 'прийти',
'в': 'в',
'шок': 'шок',
'узнав': 'узнать',
'о': 'о',
...
#######
#
# SYNTAX
#
######
>>> doc.parse_syntax(syntax_parser)
>>> display(doc.tokens[:5])
>>> doc.sents[0].syntax.print()
[DocToken(stop=5, text='Посол', id='1_1', head_id='1_7', rel='nsubj', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', id='1_2', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', id='1_3', head_id='1_4', rel='case', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', id='1_4', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', id='1_5', head_id='1_1', rel='appos', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
┌──► Посол nsubj
│ Израиля
│ ┌► на case
│ └─ Украине
│ ┌─ Йоэль
│ └► Лион flat:name
┌─────┌─└─── признался
│ │ ┌──► , punct
│ │ │ ┌► что mark
│ └►└─└─ пришел ccomp
│ │ ┌► в case
│ └──►└─ шок obl
│ ┌► , punct
│ ┌────►┌─└─ узнав advcl
│ │ │ ┌► о case
│ │ ┌───└►└─ решении obl
│ │ │ ┌─└──► властей nmod
│ │ │ │ ┌► Львовской amod
│ │ │ └──►└─ области nmod
│ └─└►┌─┌─── объявить nmod
│ │ │ ┌► 2019 amod
│ │ └►└─ год obj
│ └──►┌─ годом obl
│ ┌─────└► лидера nmod
│ │ ┌►┌─── запрещенной acl
│ │ │ │ ┌► в case
│ │ │ └►└─ России obl
│ ┌─└►└─┌─── Организации nmod
│ │ │ ┌► украинских amod
│ │ ┌─└►└─ националистов nmod
│ │ │ ┌► ( punct
│ │ └►┌─└─ ОУН parataxis
│ │ └──► ) punct
│ └──────►┌─ Степана appos
│ └► Бандеры flat:name
└──────────► . punct
...
#######
#
# NER
#
######
>>> doc.tag_ner(ner_tagger)
>>> display(doc.spans[:5])
>>> doc.ner.print()
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...]),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...]),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...]),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...]),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...])]
Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав
LOC──── LOC──── PER───────
о решении властей Львовской области объявить 2019 год годом лидера
LOC──────────────
запрещенной в России Организации украинских националистов (ОУН)
LOC─── ORG───────────────────────────────────────
Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу
PER──────────── ORG────
понять, как прославление тех, кто непосредственно принимал участие в
ужасных антисемитских преступлениях, помогает бороться с
антисемитизмом и ксенофобией. Украина не должна забывать о
LOC────
преступлениях, совершенных против украинских евреев, и никоим образом
не отмечать их через почитание их исполнителей», — написал дипломат.
11 декабря Львовский областной совет принял решение провозгласить 2019
ORG──────────────────────
год в регионе годом Степана Бандеры в связи с празднованием 110-летия
PER────────────
со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В
ORG
июле аналогичное решение принял Житомирский областной совет. В начале
ORG────────────────────────
месяца с предложением к президенту страны Петру Порошенко вернуть
PER────────────
Бандере звание Героя Украины обратились депутаты Верховной Рады.
PER──── LOC──── ORG───────────
Парламентарии уверены, что признание Бандеры национальным героем
PER────
поможет в борьбе с подрывной деятельностью против Украины в
LOC────
информационном поле, а также остановит «распространение мифов,
созданных российской пропагандой». Степан Бандера (1909-1959) был
PER───────────
одним из лидеров Организации украинских националистов, выступающей за
ORG─────────────────────────────────
создание независимого государства на территориях с украиноязычным
населением. В 2010 году в период президентства Виктора Ющенко Бандера
PER─────────── PER────
был посмертно признан Героем Украины, однако впоследствии это решение
LOC────
было отменено судом.
#######
#
# PHRASE NORM
#
#######
>>> for span in doc.spans:
>>> span.normalize(morph_vocab)
>>> display(doc.spans[:5])
>>> {_.text: _.normal for _ in doc.spans if _.text != _.normal}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион'),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Израиля': 'Израиль',
'Украине': 'Украина',
'Львовской области': 'Львовская область',
'России': 'Россия',
'Организации украинских националистов (ОУН)': 'Организация украинских националистов (ОУН)',
'Степана Бандеры': 'Степан Бандера',
'Петру Порошенко': 'Петр Порошенко',
'Бандере': 'Бандера',
'Украины': 'Украина',
'Верховной Рады': 'Верховная Рада',
'Бандеры': 'Бандера',
'Организации украинских националистов': 'Организация украинских националистов',
'Виктора Ющенко': 'Виктор Ющенко'}
#######
#
# FACT
#
######
>>> for span in doc.spans:
>>> if span.type == PER:
>>> span.extract_fact(names_extractor)
>>> display(doc.spans[:5])
>>> {_.normal: _.fact.as_dict for _ in doc.spans if _.type == PER}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион', fact=DocFact(slots=[...])),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Йоэль Лион': {'first': 'Йоэль', 'last': 'Лион'},
'Степан Бандера': {'first': 'Степан', 'last': 'Бандера'},
'Петр Порошенко': {'first': 'Петр', 'last': 'Порошенко'},
'Бандера': {'last': 'Бандера'},
'Виктор Ющенко': {'first': 'Виктор', 'last': 'Ющенко'}}
```
## Evaluation
* Segmentation — <a href="https://github.com/natasha/razdel#quality-performance">Razdel evalualtion section</a>
* Embedding — <a href="https://github.com/natasha/navec#evaluation">Navec evalualtion section</a>
* Morphology — <a href="https://github.com/natasha/slovnet#morphology-1">Slovnet Morph evaluation section</a>
* Syntax — <a href="https://github.com/natasha/slovnet#syntax-1">Slovnet Syntax evaluation section</a>
* NER — <a href="https://github.com/natasha/slovnet#ner-1">Slovnet NER evaluation section</a>
## Support
- Chat — https://telegram.me/natural_language_processing
- Issues — https://github.com/natasha/natasha/issues
- Commercial support — https://lab.alexkuk.ru
## Development
Tests:
```bash
make test
```
Package:
```bash
make version
git push
git push --tags
make clean package publish
```
Keywords: natural language processing,russian
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License

@@ -370,1 +16,372 @@ Classifier: Programming Language :: Python :: 3

Description-Content-Type: text/markdown
License-File: LICENSE
License-File: AUTHORS.md
<img src="https://github.com/natasha/natasha-logos/blob/master/natasha.svg">
![CI](https://github.com/natasha/yargy/actions/workflows/test.yml/badge.svg)
Natasha solves basic NLP tasks for Russian language: tokenization, sentence segmentation, word embedding, morphology tagging, lemmatization, phrase normalization, syntax parsing, NER tagging, fact extraction. Quality on every task is similar or better then current SOTAs for Russian language on news articles, see <a href="https://github.com/natasha/natasha#evaluation">evaluation section</a>. Natasha is not a research project, underlying technologies are built for production. We pay attention to model size, RAM usage and performance. Models run on CPU, use Numpy for inference.
Natasha integrates libraries from <a href="https://github.com/natasha">Natasha project</a> under one convenient API:
* <a href="https://github.com/natasha/razdel">Razdel</a> — token, sentence segmentation for Russian
* <a href="https://github.com/natasha/navec">Navec</a> — compact Russian embeddings
* <a href="https://github.com/natasha/slovnet">Slovnet</a> — modern deep-learning techniques for Russian NLP, compact models for Russian morphology, syntax, NER.
* <a href="https://github.com/natasha/yargy">Yargy</a> — rule-based fact extraction similar to Tomita parser.
* <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> — NLP visualizations for NER and syntax markups.
> ⚠ API may change, for realworld tasks consider using low level libraries from Natasha project.
> Models optimized for news articles, quality on other domain may be lower.
> To use old `NamesExtractor`, `AddressExtactor` downgrade `pip install natasha<1 yargy<0.13`
## Install
Natasha supports Python 3.7+ and PyPy3:
```bash
$ pip install natasha
```
## Usage
Import, initialize modules, build `Doc` object.
```python
>>> from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
PER,
NamesExtractor,
Doc
)
>>> segmenter = Segmenter()
>>> morph_vocab = MorphVocab()
>>> emb = NewsEmbedding()
>>> morph_tagger = NewsMorphTagger(emb)
>>> syntax_parser = NewsSyntaxParser(emb)
>>> ner_tagger = NewsNERTagger(emb)
>>> names_extractor = NamesExtractor(morph_vocab)
>>> text = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу понять, как прославление тех, кто непосредственно принимал участие в ужасных антисемитских преступлениях, помогает бороться с антисемитизмом и ксенофобией. Украина не должна забывать о преступлениях, совершенных против украинских евреев, и никоим образом не отмечать их через почитание их исполнителей», — написал дипломат. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В июле аналогичное решение принял Житомирский областной совет. В начале месяца с предложением к президенту страны Петру Порошенко вернуть Бандере звание Героя Украины обратились депутаты Верховной Рады. Парламентарии уверены, что признание Бандеры национальным героем поможет в борьбе с подрывной деятельностью против Украины в информационном поле, а также остановит «распространение мифов, созданных российской пропагандой». Степан Бандера (1909-1959) был одним из лидеров Организации украинских националистов, выступающей за создание независимого государства на территориях с украиноязычным населением. В 2010 году в период президентства Виктора Ющенко Бандера был посмертно признан Героем Украины, однако впоследствии это решение было отменено судом. '
>>> doc = Doc(text)
```
### Segmentation
Split text into tokens and sentencies. Defines `tokens` and `sents` properties of `doc`. Uses <a href="https://github.com/natasha/razdel">Razdel</a> internally.
```python
>>> doc.segment(segmenter)
>>> print(doc.tokens[:5])
>>> print(doc.sents[:5])
[DocToken(stop=5, text='Посол'),
DocToken(start=6, stop=13, text='Израиля'),
DocToken(start=14, stop=16, text='на'),
DocToken(start=17, stop=24, text='Украине'),
DocToken(start=25, stop=30, text='Йоэль')]
[DocSent(stop=218, text='Посол Израиля на Украине Йоэль Лион признался, чт..., tokens=[...]),
DocSent(start=219, stop=257, text='Свое заявление он разместил в Twitter.', tokens=[...]),
DocSent(start=258, stop=424, text='«Я не могу понять, как прославление тех, кто непо..., tokens=[...]),
DocSent(start=425, stop=592, text='Украина не должна забывать о преступлениях, совер..., tokens=[...]),
DocSent(start=593, stop=798, text='11 декабря Львовский областной совет принял решен..., tokens=[...])]
```
### Morphology
For every token extract rich morphology tags. Depends on <a href="#Segmentation">segmentation</a> step. Defines `pos` and `feats` properties of `doc.tokens`. Uses <a href="https://github.com/natasha/slovnet#morphology">Slovnet morphology model</a> internally.
Call `morph.print()` to visualize morphology markup.
```python
>>> doc.tag_morph(morph_tagger)
>>> print(doc.tokens[:5])
>>> doc.sents[0].morph.print()
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
Посол NOUN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Израиля PROPN|Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing
на ADP
Украине PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Йоэль PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Лион PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
признался VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Mid
, PUNCT
что SCONJ
...
```
### Lemmatization
Lemmatize every token. Depends on <a href="#Morphology">morphology</a> step. Defines `lemma` property of `doc.tokens`. Uses <a href="https://pymorphy2.readthedocs.io/en/stable/">Pymorphy</a> internally.
```python
>>> for token in doc.tokens:
>>> token.lemmatize(morph_vocab)
>>> print(doc.tokens[:5])
>>> {_.text: _.lemma for _ in doc.tokens}
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>, lemma='посол'),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>, lemma='израиль'),
DocToken(start=14, stop=16, text='на', pos='ADP', lemma='на'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>, lemma='украина'),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>, lemma='йоэль')]
{'Посол': 'посол',
'Израиля': 'израиль',
'на': 'на',
'Украине': 'украина',
'Йоэль': 'йоэль',
'Лион': 'лион',
'признался': 'признаться',
',': ',',
'что': 'что',
'пришел': 'прийти',
'в': 'в',
'шок': 'шок',
'узнав': 'узнать',
'о': 'о',
...
```
### Syntax
For every sentence run syntax analyzer. Depends on <a href="#Segmentation">segmentation</a> step. Defines `id`, `head_id`, `rel` properties of `doc.tokens`. Uses <a href="https://github.com/natasha/slovnet#syntax">Slovnet syntax model</a> internally.
Use `syntax.print()` to visualize syntax markup. Uses <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> internally.
```python
>>> doc.parse_syntax(syntax_parser)
>>> print(doc.tokens[:5])
>>> doc.sents[0].syntax.print()
[DocToken(stop=5, text='Посол', id='1_1', head_id='1_7', rel='nsubj', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', id='1_2', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', id='1_3', head_id='1_4', rel='case', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', id='1_4', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', id='1_5', head_id='1_1', rel='appos', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
┌──► Посол nsubj
│ Израиля
│ ┌► на case
│ └─ Украине
│ ┌─ Йоэль
│ └► Лион flat:name
┌─────┌─└─── признался
│ │ ┌──► , punct
│ │ │ ┌► что mark
│ └►└─└─ пришел ccomp
│ │ ┌► в case
│ └──►└─ шок obl
│ ┌► , punct
│ ┌────►┌─└─ узнав advcl
│ │ │ ┌► о case
│ │ ┌───└►└─ решении obl
│ │ │ ┌─└──► властей nmod
│ │ │ │ ┌► Львовской amod
│ │ │ └──►└─ области nmod
│ └─└►┌─┌─── объявить nmod
│ │ │ ┌► 2019 amod
│ │ └►└─ год obj
│ └──►┌─ годом obl
│ ┌─────└► лидера nmod
│ │ ┌►┌─── запрещенной acl
│ │ │ │ ┌► в case
│ │ │ └►└─ России obl
│ ┌─└►└─┌─── Организации nmod
│ │ │ ┌► украинских amod
│ │ ┌─└►└─ националистов nmod
│ │ │ ┌► ( punct
│ │ └►┌─└─ ОУН parataxis
│ │ └──► ) punct
│ └──────►┌─ Степана appos
│ └► Бандеры flat:name
└──────────► . punct
...
```
### NER
Extract standart named entities: names, locations, organizations. Depends on <a href="#Segmentation">segmentation</a> step. Defines `spans` property of `doc`. Uses <a href="https://github.com/natasha/slovnet#ner">Slovnet NER model</a> internally.
Call `ner.print()` to visualize NER markup. Uses <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> internally.
```python
>>> doc.tag_ner(ner_tagger)
>>> print(doc.spans[:5])
>>> doc.ner.print()
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...]),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...]),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...]),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...]),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...])]
Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав
LOC──── LOC──── PER───────
о решении властей Львовской области объявить 2019 год годом лидера
LOC──────────────
запрещенной в России Организации украинских националистов (ОУН)
LOC─── ORG───────────────────────────────────────
Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу
PER──────────── ORG────
понять, как прославление тех, кто непосредственно принимал участие в
ужасных антисемитских преступлениях, помогает бороться с
антисемитизмом и ксенофобией. Украина не должна забывать о
LOC────
преступлениях, совершенных против украинских евреев, и никоим образом
не отмечать их через почитание их исполнителей», — написал дипломат.
11 декабря Львовский областной совет принял решение провозгласить 2019
ORG──────────────────────
год в регионе годом Степана Бандеры в связи с празднованием 110-летия
PER────────────
со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В
ORG
июле аналогичное решение принял Житомирский областной совет. В начале
ORG────────────────────────
месяца с предложением к президенту страны Петру Порошенко вернуть
PER────────────
Бандере звание Героя Украины обратились депутаты Верховной Рады.
PER──── LOC──── ORG───────────
Парламентарии уверены, что признание Бандеры национальным героем
PER────
поможет в борьбе с подрывной деятельностью против Украины в
LOC────
информационном поле, а также остановит «распространение мифов,
созданных российской пропагандой». Степан Бандера (1909-1959) был
PER───────────
одним из лидеров Организации украинских националистов, выступающей за
ORG─────────────────────────────────
создание независимого государства на территориях с украиноязычным
населением. В 2010 году в период президентства Виктора Ющенко Бандера
PER─────────── PER────
был посмертно признан Героем Украины, однако впоследствии это решение
LOC────
было отменено судом.
```
### Named entity normalization
For every NER span apply normalization procedure. Depends on <a href="#NER">NER</a>, <a href="#Morphology">morphology</a> and <a href="#Syntax">syntax</a> steps. Defines `normal` property of `doc.spans`.
One can not just lemmatize every token inside entity span, otherwise "Организации украинских националистов" would become "Организация украинские националисты". Natasha uses syntax dependencies to produce correct "Организация украинских националистов".
```python
>>> for span in doc.spans:
>>> span.normalize(morph_vocab)
>>> print(doc.spans[:5])
>>> {_.text: _.normal for _ in doc.spans if _.text != _.normal}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион'),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Израиля': 'Израиль',
'Украине': 'Украина',
'Львовской области': 'Львовская область',
'России': 'Россия',
'Организации украинских националистов (ОУН)': 'Организация украинских националистов (ОУН)',
'Степана Бандеры': 'Степан Бандера',
'Петру Порошенко': 'Петр Порошенко',
'Бандере': 'Бандера',
'Украины': 'Украина',
'Верховной Рады': 'Верховная Рада',
'Бандеры': 'Бандера',
'Организации украинских националистов': 'Организация украинских националистов',
'Виктора Ющенко': 'Виктор Ющенко'}
```
### Named entity parsing
Parse `PER` named entities into firstname, surname and patronymic. Depends on <a href="#NER">NER</a> step. Defines `fact` property of `doc.spans`. Uses <a href="https://github.com/natasha/yargy">Yargy-parser</a> internally.
Natasha also has built in extractors for <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#DatesExtractor">dates</a>, <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#MoneyExtractor">money</a>, <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#AddrExtractor">address</a>.
```python
>>> for span in doc.spans:
>>> if span.type == PER:
>>> span.extract_fact(names_extractor)
>>> print(doc.spans[:5])
>>> {_.normal: _.fact.as_dict for _ in doc.spans if _.type == PER}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион', fact=DocFact(slots=[...])),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Йоэль Лион': {'first': 'Йоэль', 'last': 'Лион'},
'Степан Бандера': {'first': 'Степан', 'last': 'Бандера'},
'Петр Порошенко': {'first': 'Петр', 'last': 'Порошенко'},
'Бандера': {'last': 'Бандера'},
'Виктор Ющенко': {'first': 'Виктор', 'last': 'Ющенко'}}
```
## Documentation
* <a href="http://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb">Examples with description + reference</a>
* <a href="https://habr.com/ru/post/516098/#natasha">Natasha section in longread on Natasha project</a> (in Russian)
* <a href="https://youtu.be/-7XT_U6hVvk?t=951">Natasha section of Datafest 2020 talk</a> (in Russian)
## Evaluation
* Segmentation — <a href="https://github.com/natasha/razdel#quality-performance">Razdel evalualtion section</a>
* Embedding — <a href="https://github.com/natasha/navec#evaluation">Navec evalualtion section</a>
* Morphology — <a href="https://github.com/natasha/slovnet#morphology-1">Slovnet Morph evaluation section</a>
* Syntax — <a href="https://github.com/natasha/slovnet#syntax-1">Slovnet Syntax evaluation section</a>
* NER — <a href="https://github.com/natasha/slovnet#ner-1">Slovnet NER evaluation section</a>
## Support
- Chat — https://t.me/natural_language_processing
- Issues — https://github.com/natasha/natasha/issues
- Commercial support — https://lab.alexkuk.ru
## Development
Dev env
```bash
python -m venv ~/.venvs/natasha-natasha
source ~/.venvs/natasha-natasha/bin/activate
pip install -r requirements/dev.txt
pip install -e .
python -m ipykernel install --user --name natasha-natasha
```
Test
```bash
make test
```
Docs
```bash
make exec-docs
```
Release
```bash
# Update setup.py version
git commit -am 'Up version'
git tag v1.5.0
git push
git push --tags
# Github Action builds dist and publishes to PyPi
```
pymorphy2
razdel>=0.5.0
navec>=0.9.0
slovnet>=0.3.0
slovnet>=0.6.0
yargy>=0.14.0
ipymarkup>=0.8.0

@@ -0,1 +1,3 @@

AUTHORS.md
LICENSE
MANIFEST.in

@@ -5,3 +7,4 @@ README.md

setup.py
.github/workflows/main.yml
.github/workflows/pypi.yml
.github/workflows/test.yml
natasha/__init__.py

@@ -42,13 +45,10 @@ natasha/const.py

natasha/morph/vocab.py
natasha/tests/__init__.py
natasha/tests/conftest.py
natasha/tests/test_addr.py
natasha/tests/test_date.py
natasha/tests/test_doc.py
natasha/tests/test_money.py
natasha/tests/test_name.py
natasha/tests/test_span.py
notes/main.py
notes/notes.ipynb
requirements/ci.txt
requirements/main.txt
requirements/dev.txt
tests/conftest.py
tests/test_addr.py
tests/test_date.py
tests/test_doc.py
tests/test_money.py
tests/test_name.py
tests/test_span.py

@@ -18,4 +18,1 @@

from .doc import Doc # noqa
__version__ = '1.4.0'

@@ -30,2 +30,6 @@

)
Raion = fact(
'Raion',
['name', 'type']
)
Settlement = fact(

@@ -73,2 +77,6 @@ 'Settlement',

class Raion(Raion):
value = value('name')
class Settlement(Settlement):

@@ -474,3 +482,3 @@ value = value('name')

).interpretation(
Region.type.const('район')
Raion.type.const('район')
)

@@ -503,3 +511,3 @@

).interpretation(
Region.name
Raion.name
)

@@ -511,3 +519,3 @@

).interpretation(
Region
Raion
)

@@ -1826,2 +1834,4 @@

RAION,
GOROD,

@@ -1828,0 +1838,0 @@ DEREVNYA,

+372
-355
Metadata-Version: 2.1
Name: natasha
Version: 1.4.0
Version: 1.5.0
Summary: Named-entity recognition for russian language

@@ -9,357 +9,3 @@ Home-page: https://github.com/natasha/natasha

License: MIT
Description:
<img src="https://github.com/natasha/natasha-logos/blob/master/natasha.svg">
![CI](https://github.com/natasha/natasha/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/natasha/natasha/branch/master/graph/badge.svg)](https://codecov.io/gh/natasha/natasha)
Natasha solves basic NLP tasks for Russian language: tokenization, sentence segmentation, word embedding, morphology tagging, lemmatization, phrase normalization, syntax parsing, NER tagging, fact extraction. Quality on every task is similar or better then current SOTAs for Russian language on news articles, see <a href="https://github.com/natasha/natasha#evaluation">evaluation section</a>. Natasha is not a research project, underlying technologies are built for production. We pay attention to model size, RAM usage and performance. Models run on CPU, use Numpy for inference.
Natasha integrates libraries from <a href="https://github.com/natasha">Natasha project</a> under one convenient API:
* <a href="https://github.com/natasha/razdel">Razdel</a> — token, sentence segmentation for Russian
* <a href="https://github.com/natasha/navec">Navec</a> — compact Russian embeddings
* <a href="https://github.com/natasha/slovnet">Slovnet</a> — modern deep-learning techniques for Russian NLP, compact models for Russian morphology, syntax, NER.
* <a href="https://github.com/natasha/yargy">Yargy</a> — rule-based fact extraction similar to Tomita parser.
* <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> — NLP visualizations for NER and syntax markups.
> ⚠ API may change, for realworld tasks consider using low level libraries from Natasha project.
> Models optimized for news articles, quality on other domain may be lower.
> To use old `NamesExtractor`, `AddressExtactor` downgrade `pip install natasha<1 yargy<0.13`
## Install
Natasha supports Python 3.5+ and PyPy3:
```bash
$ pip install natasha
```
## Usage
For more examples and explanation see [Natasha documentation](http://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb).
```python
>>> from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
PER,
NamesExtractor,
Doc
)
#######
#
# INIT
#
#####
>>> segmenter = Segmenter()
>>> morph_vocab = MorphVocab()
>>> emb = NewsEmbedding()
>>> morph_tagger = NewsMorphTagger(emb)
>>> syntax_parser = NewsSyntaxParser(emb)
>>> ner_tagger = NewsNERTagger(emb)
>>> names_extractor = NamesExtractor(morph_vocab)
>>> text = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу понять, как прославление тех, кто непосредственно принимал участие в ужасных антисемитских преступлениях, помогает бороться с антисемитизмом и ксенофобией. Украина не должна забывать о преступлениях, совершенных против украинских евреев, и никоим образом не отмечать их через почитание их исполнителей», — написал дипломат. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В июле аналогичное решение принял Житомирский областной совет. В начале месяца с предложением к президенту страны Петру Порошенко вернуть Бандере звание Героя Украины обратились депутаты Верховной Рады. Парламентарии уверены, что признание Бандеры национальным героем поможет в борьбе с подрывной деятельностью против Украины в информационном поле, а также остановит «распространение мифов, созданных российской пропагандой». Степан Бандера (1909-1959) был одним из лидеров Организации украинских националистов, выступающей за создание независимого государства на территориях с украиноязычным населением. В 2010 году в период президентства Виктора Ющенко Бандера был посмертно признан Героем Украины, однако впоследствии это решение было отменено судом. '
>>> doc = Doc(text)
#######
#
# SEGMENT
#
#####
>>> doc.segment(segmenter)
>>> display(doc.tokens[:5])
>>> display(doc.sents[:5])
[DocToken(stop=5, text='Посол'),
DocToken(start=6, stop=13, text='Израиля'),
DocToken(start=14, stop=16, text='на'),
DocToken(start=17, stop=24, text='Украине'),
DocToken(start=25, stop=30, text='Йоэль')]
[DocSent(stop=218, text='Посол Израиля на Украине Йоэль Лион признался, чт..., tokens=[...]),
DocSent(start=219, stop=257, text='Свое заявление он разместил в Twitter.', tokens=[...]),
DocSent(start=258, stop=424, text='«Я не могу понять, как прославление тех, кто непо..., tokens=[...]),
DocSent(start=425, stop=592, text='Украина не должна забывать о преступлениях, совер..., tokens=[...]),
DocSent(start=593, stop=798, text='11 декабря Львовский областной совет принял решен..., tokens=[...])]
#######
#
# MORPH
#
#####
>>> doc.tag_morph(morph_tagger)
>>> display(doc.tokens[:5])
>>> doc.sents[0].morph.print()
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
Посол NOUN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Израиля PROPN|Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing
на ADP
Украине PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Йоэль PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Лион PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
признался VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Mid
, PUNCT
что SCONJ
...
######
#
# LEMMA
#
#######
>>> for token in doc.tokens:
>>> token.lemmatize(morph_vocab)
>>> display(doc.tokens[:5])
>>> {_.text: _.lemma for _ in doc.tokens}
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>, lemma='посол'),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>, lemma='израиль'),
DocToken(start=14, stop=16, text='на', pos='ADP', lemma='на'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>, lemma='украина'),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>, lemma='йоэль')]
{'Посол': 'посол',
'Израиля': 'израиль',
'на': 'на',
'Украине': 'украина',
'Йоэль': 'йоэль',
'Лион': 'лион',
'признался': 'признаться',
',': ',',
'что': 'что',
'пришел': 'прийти',
'в': 'в',
'шок': 'шок',
'узнав': 'узнать',
'о': 'о',
...
#######
#
# SYNTAX
#
######
>>> doc.parse_syntax(syntax_parser)
>>> display(doc.tokens[:5])
>>> doc.sents[0].syntax.print()
[DocToken(stop=5, text='Посол', id='1_1', head_id='1_7', rel='nsubj', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', id='1_2', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', id='1_3', head_id='1_4', rel='case', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', id='1_4', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', id='1_5', head_id='1_1', rel='appos', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
┌──► Посол nsubj
│ Израиля
│ ┌► на case
│ └─ Украине
│ ┌─ Йоэль
│ └► Лион flat:name
┌─────┌─└─── признался
│ │ ┌──► , punct
│ │ │ ┌► что mark
│ └►└─└─ пришел ccomp
│ │ ┌► в case
│ └──►└─ шок obl
│ ┌► , punct
│ ┌────►┌─└─ узнав advcl
│ │ │ ┌► о case
│ │ ┌───└►└─ решении obl
│ │ │ ┌─└──► властей nmod
│ │ │ │ ┌► Львовской amod
│ │ │ └──►└─ области nmod
│ └─└►┌─┌─── объявить nmod
│ │ │ ┌► 2019 amod
│ │ └►└─ год obj
│ └──►┌─ годом obl
│ ┌─────└► лидера nmod
│ │ ┌►┌─── запрещенной acl
│ │ │ │ ┌► в case
│ │ │ └►└─ России obl
│ ┌─└►└─┌─── Организации nmod
│ │ │ ┌► украинских amod
│ │ ┌─└►└─ националистов nmod
│ │ │ ┌► ( punct
│ │ └►┌─└─ ОУН parataxis
│ │ └──► ) punct
│ └──────►┌─ Степана appos
│ └► Бандеры flat:name
└──────────► . punct
...
#######
#
# NER
#
######
>>> doc.tag_ner(ner_tagger)
>>> display(doc.spans[:5])
>>> doc.ner.print()
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...]),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...]),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...]),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...]),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...])]
Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав
LOC──── LOC──── PER───────
о решении властей Львовской области объявить 2019 год годом лидера
LOC──────────────
запрещенной в России Организации украинских националистов (ОУН)
LOC─── ORG───────────────────────────────────────
Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу
PER──────────── ORG────
понять, как прославление тех, кто непосредственно принимал участие в
ужасных антисемитских преступлениях, помогает бороться с
антисемитизмом и ксенофобией. Украина не должна забывать о
LOC────
преступлениях, совершенных против украинских евреев, и никоим образом
не отмечать их через почитание их исполнителей», — написал дипломат.
11 декабря Львовский областной совет принял решение провозгласить 2019
ORG──────────────────────
год в регионе годом Степана Бандеры в связи с празднованием 110-летия
PER────────────
со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В
ORG
июле аналогичное решение принял Житомирский областной совет. В начале
ORG────────────────────────
месяца с предложением к президенту страны Петру Порошенко вернуть
PER────────────
Бандере звание Героя Украины обратились депутаты Верховной Рады.
PER──── LOC──── ORG───────────
Парламентарии уверены, что признание Бандеры национальным героем
PER────
поможет в борьбе с подрывной деятельностью против Украины в
LOC────
информационном поле, а также остановит «распространение мифов,
созданных российской пропагандой». Степан Бандера (1909-1959) был
PER───────────
одним из лидеров Организации украинских националистов, выступающей за
ORG─────────────────────────────────
создание независимого государства на территориях с украиноязычным
населением. В 2010 году в период президентства Виктора Ющенко Бандера
PER─────────── PER────
был посмертно признан Героем Украины, однако впоследствии это решение
LOC────
было отменено судом.
#######
#
# PHRASE NORM
#
#######
>>> for span in doc.spans:
>>> span.normalize(morph_vocab)
>>> display(doc.spans[:5])
>>> {_.text: _.normal for _ in doc.spans if _.text != _.normal}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион'),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Израиля': 'Израиль',
'Украине': 'Украина',
'Львовской области': 'Львовская область',
'России': 'Россия',
'Организации украинских националистов (ОУН)': 'Организация украинских националистов (ОУН)',
'Степана Бандеры': 'Степан Бандера',
'Петру Порошенко': 'Петр Порошенко',
'Бандере': 'Бандера',
'Украины': 'Украина',
'Верховной Рады': 'Верховная Рада',
'Бандеры': 'Бандера',
'Организации украинских националистов': 'Организация украинских националистов',
'Виктора Ющенко': 'Виктор Ющенко'}
#######
#
# FACT
#
######
>>> for span in doc.spans:
>>> if span.type == PER:
>>> span.extract_fact(names_extractor)
>>> display(doc.spans[:5])
>>> {_.normal: _.fact.as_dict for _ in doc.spans if _.type == PER}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион', fact=DocFact(slots=[...])),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Йоэль Лион': {'first': 'Йоэль', 'last': 'Лион'},
'Степан Бандера': {'first': 'Степан', 'last': 'Бандера'},
'Петр Порошенко': {'first': 'Петр', 'last': 'Порошенко'},
'Бандера': {'last': 'Бандера'},
'Виктор Ющенко': {'first': 'Виктор', 'last': 'Ющенко'}}
```
## Evaluation
* Segmentation — <a href="https://github.com/natasha/razdel#quality-performance">Razdel evalualtion section</a>
* Embedding — <a href="https://github.com/natasha/navec#evaluation">Navec evalualtion section</a>
* Morphology — <a href="https://github.com/natasha/slovnet#morphology-1">Slovnet Morph evaluation section</a>
* Syntax — <a href="https://github.com/natasha/slovnet#syntax-1">Slovnet Syntax evaluation section</a>
* NER — <a href="https://github.com/natasha/slovnet#ner-1">Slovnet NER evaluation section</a>
## Support
- Chat — https://telegram.me/natural_language_processing
- Issues — https://github.com/natasha/natasha/issues
- Commercial support — https://lab.alexkuk.ru
## Development
Tests:
```bash
make test
```
Package:
```bash
make version
git push
git push --tags
make clean package publish
```
Keywords: natural language processing,russian
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License

@@ -370,1 +16,372 @@ Classifier: Programming Language :: Python :: 3

Description-Content-Type: text/markdown
License-File: LICENSE
License-File: AUTHORS.md
<img src="https://github.com/natasha/natasha-logos/blob/master/natasha.svg">
![CI](https://github.com/natasha/yargy/actions/workflows/test.yml/badge.svg)
Natasha solves basic NLP tasks for Russian language: tokenization, sentence segmentation, word embedding, morphology tagging, lemmatization, phrase normalization, syntax parsing, NER tagging, fact extraction. Quality on every task is similar or better then current SOTAs for Russian language on news articles, see <a href="https://github.com/natasha/natasha#evaluation">evaluation section</a>. Natasha is not a research project, underlying technologies are built for production. We pay attention to model size, RAM usage and performance. Models run on CPU, use Numpy for inference.
Natasha integrates libraries from <a href="https://github.com/natasha">Natasha project</a> under one convenient API:
* <a href="https://github.com/natasha/razdel">Razdel</a> — token, sentence segmentation for Russian
* <a href="https://github.com/natasha/navec">Navec</a> — compact Russian embeddings
* <a href="https://github.com/natasha/slovnet">Slovnet</a> — modern deep-learning techniques for Russian NLP, compact models for Russian morphology, syntax, NER.
* <a href="https://github.com/natasha/yargy">Yargy</a> — rule-based fact extraction similar to Tomita parser.
* <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> — NLP visualizations for NER and syntax markups.
> ⚠ API may change, for realworld tasks consider using low level libraries from Natasha project.
> Models optimized for news articles, quality on other domain may be lower.
> To use old `NamesExtractor`, `AddressExtactor` downgrade `pip install natasha<1 yargy<0.13`
## Install
Natasha supports Python 3.7+ and PyPy3:
```bash
$ pip install natasha
```
## Usage
Import, initialize modules, build `Doc` object.
```python
>>> from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
PER,
NamesExtractor,
Doc
)
>>> segmenter = Segmenter()
>>> morph_vocab = MorphVocab()
>>> emb = NewsEmbedding()
>>> morph_tagger = NewsMorphTagger(emb)
>>> syntax_parser = NewsSyntaxParser(emb)
>>> ner_tagger = NewsNERTagger(emb)
>>> names_extractor = NamesExtractor(morph_vocab)
>>> text = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу понять, как прославление тех, кто непосредственно принимал участие в ужасных антисемитских преступлениях, помогает бороться с антисемитизмом и ксенофобией. Украина не должна забывать о преступлениях, совершенных против украинских евреев, и никоим образом не отмечать их через почитание их исполнителей», — написал дипломат. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В июле аналогичное решение принял Житомирский областной совет. В начале месяца с предложением к президенту страны Петру Порошенко вернуть Бандере звание Героя Украины обратились депутаты Верховной Рады. Парламентарии уверены, что признание Бандеры национальным героем поможет в борьбе с подрывной деятельностью против Украины в информационном поле, а также остановит «распространение мифов, созданных российской пропагандой». Степан Бандера (1909-1959) был одним из лидеров Организации украинских националистов, выступающей за создание независимого государства на территориях с украиноязычным населением. В 2010 году в период президентства Виктора Ющенко Бандера был посмертно признан Героем Украины, однако впоследствии это решение было отменено судом. '
>>> doc = Doc(text)
```
### Segmentation
Split text into tokens and sentencies. Defines `tokens` and `sents` properties of `doc`. Uses <a href="https://github.com/natasha/razdel">Razdel</a> internally.
```python
>>> doc.segment(segmenter)
>>> print(doc.tokens[:5])
>>> print(doc.sents[:5])
[DocToken(stop=5, text='Посол'),
DocToken(start=6, stop=13, text='Израиля'),
DocToken(start=14, stop=16, text='на'),
DocToken(start=17, stop=24, text='Украине'),
DocToken(start=25, stop=30, text='Йоэль')]
[DocSent(stop=218, text='Посол Израиля на Украине Йоэль Лион признался, чт..., tokens=[...]),
DocSent(start=219, stop=257, text='Свое заявление он разместил в Twitter.', tokens=[...]),
DocSent(start=258, stop=424, text='«Я не могу понять, как прославление тех, кто непо..., tokens=[...]),
DocSent(start=425, stop=592, text='Украина не должна забывать о преступлениях, совер..., tokens=[...]),
DocSent(start=593, stop=798, text='11 декабря Львовский областной совет принял решен..., tokens=[...])]
```
### Morphology
For every token extract rich morphology tags. Depends on <a href="#Segmentation">segmentation</a> step. Defines `pos` and `feats` properties of `doc.tokens`. Uses <a href="https://github.com/natasha/slovnet#morphology">Slovnet morphology model</a> internally.
Call `morph.print()` to visualize morphology markup.
```python
>>> doc.tag_morph(morph_tagger)
>>> print(doc.tokens[:5])
>>> doc.sents[0].morph.print()
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
Посол NOUN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Израиля PROPN|Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing
на ADP
Украине PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Йоэль PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Лион PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
признался VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Mid
, PUNCT
что SCONJ
...
```
### Lemmatization
Lemmatize every token. Depends on <a href="#Morphology">morphology</a> step. Defines `lemma` property of `doc.tokens`. Uses <a href="https://pymorphy2.readthedocs.io/en/stable/">Pymorphy</a> internally.
```python
>>> for token in doc.tokens:
>>> token.lemmatize(morph_vocab)
>>> print(doc.tokens[:5])
>>> {_.text: _.lemma for _ in doc.tokens}
[DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>, lemma='посол'),
DocToken(start=6, stop=13, text='Израиля', pos='PROPN', feats=<Inan,Gen,Masc,Sing>, lemma='израиль'),
DocToken(start=14, stop=16, text='на', pos='ADP', lemma='на'),
DocToken(start=17, stop=24, text='Украине', pos='PROPN', feats=<Inan,Loc,Fem,Sing>, lemma='украина'),
DocToken(start=25, stop=30, text='Йоэль', pos='PROPN', feats=<Anim,Nom,Masc,Sing>, lemma='йоэль')]
{'Посол': 'посол',
'Израиля': 'израиль',
'на': 'на',
'Украине': 'украина',
'Йоэль': 'йоэль',
'Лион': 'лион',
'признался': 'признаться',
',': ',',
'что': 'что',
'пришел': 'прийти',
'в': 'в',
'шок': 'шок',
'узнав': 'узнать',
'о': 'о',
...
```
### Syntax
For every sentence run syntax analyzer. Depends on <a href="#Segmentation">segmentation</a> step. Defines `id`, `head_id`, `rel` properties of `doc.tokens`. Uses <a href="https://github.com/natasha/slovnet#syntax">Slovnet syntax model</a> internally.
Use `syntax.print()` to visualize syntax markup. Uses <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> internally.
```python
>>> doc.parse_syntax(syntax_parser)
>>> print(doc.tokens[:5])
>>> doc.sents[0].syntax.print()
[DocToken(stop=5, text='Посол', id='1_1', head_id='1_7', rel='nsubj', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),
DocToken(start=6, stop=13, text='Израиля', id='1_2', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Gen,Masc,Sing>),
DocToken(start=14, stop=16, text='на', id='1_3', head_id='1_4', rel='case', pos='ADP'),
DocToken(start=17, stop=24, text='Украине', id='1_4', head_id='1_1', rel='nmod', pos='PROPN', feats=<Inan,Loc,Fem,Sing>),
DocToken(start=25, stop=30, text='Йоэль', id='1_5', head_id='1_1', rel='appos', pos='PROPN', feats=<Anim,Nom,Masc,Sing>)]
┌──► Посол nsubj
│ Израиля
│ ┌► на case
│ └─ Украине
│ ┌─ Йоэль
│ └► Лион flat:name
┌─────┌─└─── признался
│ │ ┌──► , punct
│ │ │ ┌► что mark
│ └►└─└─ пришел ccomp
│ │ ┌► в case
│ └──►└─ шок obl
│ ┌► , punct
│ ┌────►┌─└─ узнав advcl
│ │ │ ┌► о case
│ │ ┌───└►└─ решении obl
│ │ │ ┌─└──► властей nmod
│ │ │ │ ┌► Львовской amod
│ │ │ └──►└─ области nmod
│ └─└►┌─┌─── объявить nmod
│ │ │ ┌► 2019 amod
│ │ └►└─ год obj
│ └──►┌─ годом obl
│ ┌─────└► лидера nmod
│ │ ┌►┌─── запрещенной acl
│ │ │ │ ┌► в case
│ │ │ └►└─ России obl
│ ┌─└►└─┌─── Организации nmod
│ │ │ ┌► украинских amod
│ │ ┌─└►└─ националистов nmod
│ │ │ ┌► ( punct
│ │ └►┌─└─ ОУН parataxis
│ │ └──► ) punct
│ └──────►┌─ Степана appos
│ └► Бандеры flat:name
└──────────► . punct
...
```
### NER
Extract standart named entities: names, locations, organizations. Depends on <a href="#Segmentation">segmentation</a> step. Defines `spans` property of `doc`. Uses <a href="https://github.com/natasha/slovnet#ner">Slovnet NER model</a> internally.
Call `ner.print()` to visualize NER markup. Uses <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> internally.
```python
>>> doc.tag_ner(ner_tagger)
>>> print(doc.spans[:5])
>>> doc.ner.print()
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...]),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...]),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...]),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...]),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...])]
Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав
LOC──── LOC──── PER───────
о решении властей Львовской области объявить 2019 год годом лидера
LOC──────────────
запрещенной в России Организации украинских националистов (ОУН)
LOC─── ORG───────────────────────────────────────
Степана Бандеры. Свое заявление он разместил в Twitter. «Я не могу
PER──────────── ORG────
понять, как прославление тех, кто непосредственно принимал участие в
ужасных антисемитских преступлениях, помогает бороться с
антисемитизмом и ксенофобией. Украина не должна забывать о
LOC────
преступлениях, совершенных против украинских евреев, и никоим образом
не отмечать их через почитание их исполнителей», — написал дипломат.
11 декабря Львовский областной совет принял решение провозгласить 2019
ORG──────────────────────
год в регионе годом Степана Бандеры в связи с празднованием 110-летия
PER────────────
со дня рождения лидера ОУН (Бандера родился 1 января 1909 года). В
ORG
июле аналогичное решение принял Житомирский областной совет. В начале
ORG────────────────────────
месяца с предложением к президенту страны Петру Порошенко вернуть
PER────────────
Бандере звание Героя Украины обратились депутаты Верховной Рады.
PER──── LOC──── ORG───────────
Парламентарии уверены, что признание Бандеры национальным героем
PER────
поможет в борьбе с подрывной деятельностью против Украины в
LOC────
информационном поле, а также остановит «распространение мифов,
созданных российской пропагандой». Степан Бандера (1909-1959) был
PER───────────
одним из лидеров Организации украинских националистов, выступающей за
ORG─────────────────────────────────
создание независимого государства на территориях с украиноязычным
населением. В 2010 году в период президентства Виктора Ющенко Бандера
PER─────────── PER────
был посмертно признан Героем Украины, однако впоследствии это решение
LOC────
было отменено судом.
```
### Named entity normalization
For every NER span apply normalization procedure. Depends on <a href="#NER">NER</a>, <a href="#Morphology">morphology</a> and <a href="#Syntax">syntax</a> steps. Defines `normal` property of `doc.spans`.
One can not just lemmatize every token inside entity span, otherwise "Организации украинских националистов" would become "Организация украинские националисты". Natasha uses syntax dependencies to produce correct "Организация украинских националистов".
```python
>>> for span in doc.spans:
>>> span.normalize(morph_vocab)
>>> print(doc.spans[:5])
>>> {_.text: _.normal for _ in doc.spans if _.text != _.normal}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион'),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Израиля': 'Израиль',
'Украине': 'Украина',
'Львовской области': 'Львовская область',
'России': 'Россия',
'Организации украинских националистов (ОУН)': 'Организация украинских националистов (ОУН)',
'Степана Бандеры': 'Степан Бандера',
'Петру Порошенко': 'Петр Порошенко',
'Бандере': 'Бандера',
'Украины': 'Украина',
'Верховной Рады': 'Верховная Рада',
'Бандеры': 'Бандера',
'Организации украинских националистов': 'Организация украинских националистов',
'Виктора Ющенко': 'Виктор Ющенко'}
```
### Named entity parsing
Parse `PER` named entities into firstname, surname and patronymic. Depends on <a href="#NER">NER</a> step. Defines `fact` property of `doc.spans`. Uses <a href="https://github.com/natasha/yargy">Yargy-parser</a> internally.
Natasha also has built in extractors for <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#DatesExtractor">dates</a>, <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#MoneyExtractor">money</a>, <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#AddrExtractor">address</a>.
```python
>>> for span in doc.spans:
>>> if span.type == PER:
>>> span.extract_fact(names_extractor)
>>> print(doc.spans[:5])
>>> {_.normal: _.fact.as_dict for _ in doc.spans if _.type == PER}
[DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),
DocSpan(start=17, stop=24, type='LOC', text='Украине', tokens=[...], normal='Украина'),
DocSpan(start=25, stop=35, type='PER', text='Йоэль Лион', tokens=[...], normal='Йоэль Лион', fact=DocFact(slots=[...])),
DocSpan(start=89, stop=106, type='LOC', text='Львовской области', tokens=[...], normal='Львовская область'),
DocSpan(start=152, stop=158, type='LOC', text='России', tokens=[...], normal='Россия')]
{'Йоэль Лион': {'first': 'Йоэль', 'last': 'Лион'},
'Степан Бандера': {'first': 'Степан', 'last': 'Бандера'},
'Петр Порошенко': {'first': 'Петр', 'last': 'Порошенко'},
'Бандера': {'last': 'Бандера'},
'Виктор Ющенко': {'first': 'Виктор', 'last': 'Ющенко'}}
```
## Documentation
* <a href="http://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb">Examples with description + reference</a>
* <a href="https://habr.com/ru/post/516098/#natasha">Natasha section in longread on Natasha project</a> (in Russian)
* <a href="https://youtu.be/-7XT_U6hVvk?t=951">Natasha section of Datafest 2020 talk</a> (in Russian)
## Evaluation
* Segmentation — <a href="https://github.com/natasha/razdel#quality-performance">Razdel evalualtion section</a>
* Embedding — <a href="https://github.com/natasha/navec#evaluation">Navec evalualtion section</a>
* Morphology — <a href="https://github.com/natasha/slovnet#morphology-1">Slovnet Morph evaluation section</a>
* Syntax — <a href="https://github.com/natasha/slovnet#syntax-1">Slovnet Syntax evaluation section</a>
* NER — <a href="https://github.com/natasha/slovnet#ner-1">Slovnet NER evaluation section</a>
## Support
- Chat — https://t.me/natural_language_processing
- Issues — https://github.com/natasha/natasha/issues
- Commercial support — https://lab.alexkuk.ru
## Development
Dev env
```bash
python -m venv ~/.venvs/natasha-natasha
source ~/.venvs/natasha-natasha/bin/activate
pip install -r requirements/dev.txt
pip install -e .
python -m ipykernel install --user --name natasha-natasha
```
Test
```bash
make test
```
Docs
```bash
make exec-docs
```
Release
```bash
# Update setup.py version
git commit -am 'Up version'
git tag v1.5.0
git push
git push --tags
# Github Action builds dist and publishes to PyPi
```
+77
-61
<img src="https://github.com/natasha/natasha-logos/blob/master/natasha.svg">
![CI](https://github.com/natasha/natasha/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/natasha/natasha/branch/master/graph/badge.svg)](https://codecov.io/gh/natasha/natasha)
![CI](https://github.com/natasha/yargy/actions/workflows/test.yml/badge.svg)

@@ -22,3 +22,3 @@ Natasha solves basic NLP tasks for Russian language: tokenization, sentence segmentation, word embedding, morphology tagging, lemmatization, phrase normalization, syntax parsing, NER tagging, fact extraction. Quality on every task is similar or better then current SOTAs for Russian language on news articles, see <a href="https://github.com/natasha/natasha#evaluation">evaluation section</a>. Natasha is not a research project, underlying technologies are built for production. We pay attention to model size, RAM usage and performance. Models run on CPU, use Numpy for inference.

Natasha supports Python 3.5+ and PyPy3:
Natasha supports Python 3.7+ and PyPy3:

@@ -31,3 +31,3 @@ ```bash

For more examples and explanation see [Natasha documentation](http://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb).
Import, initialize modules, build `Doc` object.

@@ -51,9 +51,2 @@ ```python

#######
#
# INIT
#
#####
>>> segmenter = Segmenter()

@@ -71,14 +64,12 @@ >>> morph_vocab = MorphVocab()

>>> doc = Doc(text)
```
### Segmentation
#######
#
# SEGMENT
#
#####
Split text into tokens and sentencies. Defines `tokens` and `sents` properties of `doc`. Uses <a href="https://github.com/natasha/razdel">Razdel</a> internally.
```python
>>> doc.segment(segmenter)
>>> display(doc.tokens[:5])
>>> display(doc.sents[:5])
>>> print(doc.tokens[:5])
>>> print(doc.sents[:5])
[DocToken(stop=5, text='Посол'),

@@ -94,13 +85,13 @@ DocToken(start=6, stop=13, text='Израиля'),

DocSent(start=593, stop=798, text='11 декабря Львовский областной совет принял решен..., tokens=[...])]
```
### Morphology
#######
#
# MORPH
#
#####
For every token extract rich morphology tags. Depends on <a href="#Segmentation">segmentation</a> step. Defines `pos` and `feats` properties of `doc.tokens`. Uses <a href="https://github.com/natasha/slovnet#morphology">Slovnet morphology model</a> internally.
Call `morph.print()` to visualize morphology markup.
```python
>>> doc.tag_morph(morph_tagger)
>>> display(doc.tokens[:5])
>>> print(doc.tokens[:5])
>>> doc.sents[0].morph.print()

@@ -122,15 +113,13 @@ [DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),

...
```
### Lemmatization
######
#
# LEMMA
#
#######
Lemmatize every token. Depends on <a href="#Morphology">morphology</a> step. Defines `lemma` property of `doc.tokens`. Uses <a href="https://pymorphy2.readthedocs.io/en/stable/">Pymorphy</a> internally.
```python
>>> for token in doc.tokens:
>>> token.lemmatize(morph_vocab)
>>> display(doc.tokens[:5])
>>> print(doc.tokens[:5])
>>> {_.text: _.lemma for _ in doc.tokens}

@@ -157,13 +146,13 @@ [DocToken(stop=5, text='Посол', pos='NOUN', feats=<Anim,Nom,Masc,Sing>, lemma='посол'),

...
```
### Syntax
#######
#
# SYNTAX
#
######
For every sentence run syntax analyzer. Depends on <a href="#Segmentation">segmentation</a> step. Defines `id`, `head_id`, `rel` properties of `doc.tokens`. Uses <a href="https://github.com/natasha/slovnet#syntax">Slovnet syntax model</a> internally.
Use `syntax.print()` to visualize syntax markup. Uses <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> internally.
```python
>>> doc.parse_syntax(syntax_parser)
>>> display(doc.tokens[:5])
>>> print(doc.tokens[:5])
>>> doc.sents[0].syntax.print()

@@ -212,13 +201,13 @@ [DocToken(stop=5, text='Посол', id='1_1', head_id='1_7', rel='nsubj', pos='NOUN', feats=<Anim,Nom,Masc,Sing>),

...
```
### NER
#######
#
# NER
#
######
Extract standart named entities: names, locations, organizations. Depends on <a href="#Segmentation">segmentation</a> step. Defines `spans` property of `doc`. Uses <a href="https://github.com/natasha/slovnet#ner">Slovnet NER model</a> internally.
Call `ner.print()` to visualize NER markup. Uses <a href="https://github.com/natasha/ipymarkup">Ipymarkup</a> internally.
```python
>>> doc.tag_ner(ner_tagger)
>>> display(doc.spans[:5])
>>> print(doc.spans[:5])
>>> doc.ner.print()

@@ -271,14 +260,14 @@ [DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...]),

было отменено судом.
```
### Named entity normalization
#######
#
# PHRASE NORM
#
#######
For every NER span apply normalization procedure. Depends on <a href="#NER">NER</a>, <a href="#Morphology">morphology</a> and <a href="#Syntax">syntax</a> steps. Defines `normal` property of `doc.spans`.
One can not just lemmatize every token inside entity span, otherwise "Организации украинских националистов" would become "Организация украинские националисты". Natasha uses syntax dependencies to produce correct "Организация украинских националистов".
```python
>>> for span in doc.spans:
>>> span.normalize(morph_vocab)
>>> display(doc.spans[:5])
>>> print(doc.spans[:5])
>>> {_.text: _.normal for _ in doc.spans if _.text != _.normal}

@@ -303,11 +292,11 @@ [DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),

'Виктора Ющенко': 'Виктор Ющенко'}
```
### Named entity parsing
#######
#
# FACT
#
######
Parse `PER` named entities into firstname, surname and patronymic. Depends on <a href="#NER">NER</a> step. Defines `fact` property of `doc.spans`. Uses <a href="https://github.com/natasha/yargy">Yargy-parser</a> internally.
Natasha also has built in extractors for <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#DatesExtractor">dates</a>, <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#MoneyExtractor">money</a>, <a href="https://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb#AddrExtractor">address</a>.
```python
>>> for span in doc.spans:

@@ -317,3 +306,3 @@ >>> if span.type == PER:

>>> display(doc.spans[:5])
>>> print(doc.spans[:5])
>>> {_.normal: _.fact.as_dict for _ in doc.spans if _.type == PER}

@@ -330,5 +319,10 @@ [DocSpan(start=6, stop=13, type='LOC', text='Израиля', tokens=[...], normal='Израиль'),

'Виктор Ющенко': {'first': 'Виктор', 'last': 'Ющенко'}}
```
## Documentation
* <a href="http://nbviewer.jupyter.org/github/natasha/natasha/blob/master/docs.ipynb">Examples with description + reference</a>
* <a href="https://habr.com/ru/post/516098/#natasha">Natasha section in longread on Natasha project</a> (in Russian)
* <a href="https://youtu.be/-7XT_U6hVvk?t=951">Natasha section of Datafest 2020 talk</a> (in Russian)
## Evaluation

@@ -344,3 +338,3 @@

- Chat — https://telegram.me/natural_language_processing
- Chat — https://t.me/natural_language_processing
- Issues — https://github.com/natasha/natasha/issues

@@ -351,16 +345,38 @@ - Commercial support — https://lab.alexkuk.ru

Tests:
Dev env
```bash
python -m venv ~/.venvs/natasha-natasha
source ~/.venvs/natasha-natasha/bin/activate
pip install -r requirements/dev.txt
pip install -e .
python -m ipykernel install --user --name natasha-natasha
```
Test
```bash
make test
```
Package:
Docs
```bash
make version
make exec-docs
```
Release
```bash
# Update setup.py version
git commit -am 'Up version'
git tag v1.5.0
git push
git push --tags
make clean package publish
# Github Action builds dist and publishes to PyPi
```

@@ -1,2 +0,3 @@

pymorphy2==0.8
pymorphy2==0.9.1
pymorphy2-dicts-ru==2.4.417127.4579844

@@ -9,6 +10,3 @@ razdel==0.5.0

pytest==5.2.2
pytest-cov==2.8.1
pytest-flakes==4.0.0
pytest-pep8==1.0.6
nbval==0.9.5
pytest==7.2.1
flake8==5.0.4

@@ -1,18 +0,7 @@

[bumpversion]
current_version = 1.4.0
files = setup.py natasha/__init__.py
commit = True
message = Up version
tag = True
[tool:pytest]
python_files = test_*.py test.py
pep8ignore =
E501 # E501 line too long
W503 # W503 and all(
markers =
pep8
filterwarnings =
ignore::DeprecationWarning
[flake8]
extend-ignore = E501,W503
[egg_info]

@@ -19,0 +8,0 @@ tag_build =

@@ -9,9 +9,5 @@

with open('requirements/main.txt') as file:
requirements = [_.strip() for _ in file]
setup(
name='natasha',
version='1.4.0',
version='1.5.0',

@@ -35,3 +31,5 @@ description='Named-entity recognition for russian language',

packages=find_packages(),
packages=find_packages(
exclude=['tests']
),
package_data={

@@ -44,3 +42,10 @@ 'natasha': [

},
install_requires=requirements
install_requires=[
'pymorphy2',
'razdel>=0.5.0',
'navec>=0.9.0',
'slovnet>=0.6.0',
'yargy>=0.14.0',
'ipymarkup>=0.8.0',
]
)
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements/ci.txt
- name: Test
run: make test
- uses: codecov/codecov-action@v1
import pytest
from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
NamesExtractor,
DatesExtractor,
MoneyExtractor,
AddrExtractor,
)
@pytest.fixture(scope='session')
def segmenter():
return Segmenter()
@pytest.fixture(scope='session')
def morph_vocab():
return MorphVocab()
@pytest.fixture(scope='session')
def embedding():
return NewsEmbedding()
@pytest.fixture(scope='session')
def morph_tagger(embedding):
return NewsMorphTagger(embedding)
@pytest.fixture(scope='session')
def syntax_parser(embedding):
return NewsSyntaxParser(embedding)
@pytest.fixture(scope='session')
def ner_tagger(embedding):
return NewsNERTagger(embedding)
@pytest.fixture(scope='session')
def names_extractor(morph_vocab):
return NamesExtractor(morph_vocab)
@pytest.fixture(scope='session')
def dates_extractor(morph_vocab):
return DatesExtractor(morph_vocab)
@pytest.fixture(scope='session')
def money_extractor(morph_vocab):
return MoneyExtractor(morph_vocab)
@pytest.fixture(scope='session')
def addr_extractor(morph_vocab):
return AddrExtractor(morph_vocab)
import pytest
from natasha.obj import (
AddrPart as Part,
Addr
)
tests = [
[
'Россия, Вологодская обл. г. Череповец, пр.Победы 93 б',
Addr([
Part('Россия', 'страна'),
Part('Вологодская', 'область'),
Part('Череповец', 'город'),
Part('Победы', 'проспект'),
])
],
[
'692909, РФ, Приморский край, г. Находка, ул. Добролюбова, 18',
Addr([
Part('692909', 'индекс'),
Part('РФ', 'страна'),
Part('Приморский', 'край'),
Part('Находка', 'город'),
Part('Добролюбова', 'улица'),
])
],
[
'д. Федоровка, ул. Дружбы, 13',
Addr([
Part('Федоровка', 'деревня'),
Part('Дружбы', 'улица'),
])
],
[
'Россия, 129110, г.Москва, Олимпийский проспект, 22',
Addr([
Part('Россия', 'страна'),
Part('129110', 'индекс'),
Part('Москва', 'город'),
Part('Олимпийский', 'проспект'),
])
],
[
'г. Санкт-Петербург, Красногвардейский пер., д. 15',
Addr([
Part('Санкт-Петербург', 'город'),
Part('Красногвардейский', 'переулок'),
Part('15', 'дом')
])
],
[
'Республика Карелия,г.Петрозаводск,ул.Маршала Мерецкова, д.8 Б,офис 4',
Addr([
Part('Карелия', 'республика'),
Part('Петрозаводск', 'город'),
Part('Маршала Мерецкова', 'улица'),
Part('8 Б', 'дом'),
Part('4', 'офис')
])
],
[
'628000, ХМАО-Югра, г.Ханты-Мансийск, ул. Ледовая , д.19',
Addr([
Part('628000', 'индекс'),
Part('ХМАО-Югра'),
Part('Ханты-Мансийск', 'город'),
Part('Ледовая', 'улица'),
Part('19', 'дом')
])
],
[
'ХМАО г.Нижневартовск пер.Ягельный 17',
Addr([
Part('ХМАО'),
Part('Нижневартовск', 'город'),
Part('Ягельный', 'переулок'),
])
],
[
'Белгородская обл, пгт Борисовка,ул. Рудого д.160',
Addr([
Part('Белгородская', 'область'),
Part('Борисовка', 'посёлок'),
Part('Рудого', 'улица'),
Part('160', 'дом')
])
],
[
'Самарская область, п.г.т. Алексеевка, ул. Ульяновская д. 21',
Addr([
Part('Самарская', 'область'),
Part('Алексеевка', 'посёлок'),
Part('Ульяновская', 'улица'),
Part('21', 'дом')
])
],
[
'Мурманская обл поселок городского типа Молочный, ул.Гальченко д.11',
Addr([
Part('Мурманская', 'область'),
Part('Молочный', 'посёлок'),
Part('Гальченко', 'улица'),
Part('11', 'дом')
])
],
[
'ул. Народного Ополчения д. 9к.3',
Addr([
Part('Народного Ополчения', 'улица'),
Part('9к', 'дом'),
])
],
[
'ул. Б. Пироговская, д.37/430',
Addr([
Part('Б. Пироговская', 'улица'),
Part('37/430', 'дом')
])
],
]
@pytest.mark.parametrize('test', tests)
def test_extractor(addr_extractor, test):
text, target = test
pred = addr_extractor.find(text).fact
assert pred == target
import pytest
from natasha.obj import Date
tests = [
[
'24.01.2017',
Date(2017, 1, 24)
],
[
'27. 05.99',
Date(1999, 5, 27)
],
[
'2015 год',
Date(2015)
],
[
'2014 г',
Date(2014)
],
[
'1 апреля',
Date(None, 4, 1)
],
[
'май 2017 г.',
Date(2017, 5)
],
[
'9 мая 2017 года',
Date(2017, 5, 9)
],
]
@pytest.mark.parametrize('test', tests)
def test_extractor(dates_extractor, test):
text, target = test
pred = dates_extractor.find(text).fact
assert pred == target
import re
from natasha import PER, Doc
TEXT = 'Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав о решении властей Львовской области объявить 2019 год годом лидера запрещенной в России Организации украинских националистов (ОУН) Степана Бандеры. Свое заявление он разместил в Twitter. 11 декабря Львовский областной совет принял решение провозгласить 2019 год в регионе годом Степана Бандеры в связи с празднованием 110-летия со дня рождения лидера ОУН (Бандера родился 1 января 1909 года).'
def strip(markup):
markup = markup.lstrip('\n')
return re.sub(r'\s+\n', '\n', markup)
NER = strip('''
Посол Израиля на Украине Йоэль Лион признался, что пришел в шок, узнав
LOC──── LOC──── PER───────
о решении властей Львовской области объявить 2019 год годом лидера
LOC──────────────
запрещенной в России Организации украинских националистов (ОУН)
LOC─── ORG───────────────────────────────────────
Степана Бандеры. Свое заявление он разместил в Twitter. 11 декабря
PER──────────── ORG────
Львовский областной совет принял решение провозгласить 2019 год в
ORG──────────────────────
регионе годом Степана Бандеры в связи с празднованием 110-летия со дня
PER────────────
рождения лидера ОУН (Бандера родился 1 января 1909 года).
ORG
''')
MORPH = strip('''
Посол NOUN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Израиля PROPN|Animacy=Inan|Case=Gen|Gender=Masc|Number=Sing
на ADP
Украине PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Йоэль PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
Лион PROPN|Animacy=Anim|Case=Nom|Gender=Masc|Number=Sing
признался VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Mid
, PUNCT
что SCONJ
пришел VERB|Aspect=Perf|Gender=Masc|Mood=Ind|Number=Sing|Tense=Past|VerbForm=Fin|Voice=Act
в ADP
шок NOUN|Animacy=Inan|Case=Acc|Gender=Masc|Number=Sing
, PUNCT
узнав VERB|Aspect=Perf|Tense=Past|VerbForm=Conv|Voice=Act
о ADP
решении NOUN|Animacy=Inan|Case=Loc|Gender=Neut|Number=Sing
властей NOUN|Animacy=Inan|Case=Gen|Gender=Fem|Number=Plur
Львовской ADJ|Case=Gen|Degree=Pos|Gender=Fem|Number=Sing
области NOUN|Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing
объявить VERB|Aspect=Perf|VerbForm=Inf|Voice=Act
2019 ADJ
год NOUN|Animacy=Inan|Case=Acc|Gender=Masc|Number=Sing
годом NOUN|Animacy=Inan|Case=Ins|Gender=Masc|Number=Sing
лидера NOUN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Sing
запрещенной VERB|Aspect=Perf|Case=Gen|Gender=Fem|Number=Sing|Tense=Past|VerbForm=Part|Voice=Pass
в ADP
России PROPN|Animacy=Inan|Case=Loc|Gender=Fem|Number=Sing
Организации PROPN|Animacy=Inan|Case=Gen|Gender=Fem|Number=Sing
украинских ADJ|Case=Gen|Degree=Pos|Number=Plur
националистов NOUN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Plur
( PUNCT
ОУН PROPN|Animacy=Inan|Case=Nom|Gender=Fem|Number=Sing
) PUNCT
Степана PROPN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Sing
Бандеры PROPN|Animacy=Anim|Case=Gen|Gender=Masc|Number=Sing
. PUNCT
''')
SYNTAX = strip('''
┌──► Посол nsubj
│ Израиля
│ ┌► на case
│ └─ Украине
│ ┌─ Йоэль
│ └► Лион flat:name
┌─────┌─└─── признался
│ │ ┌──► , punct
│ │ │ ┌► что mark
│ └►└─└─ пришел ccomp
│ │ ┌► в case
│ └──►└─ шок obl
│ ┌► , punct
│ ┌────►┌─└─ узнав advcl
│ │ │ ┌► о case
│ │ ┌───└►└─ решении obl
│ │ │ ┌─└──► властей nmod
│ │ │ │ ┌► Львовской amod
│ │ │ └──►└─ области nmod
│ └─└►┌─┌─── объявить nmod
│ │ │ ┌► 2019 amod
│ │ └►└─ год obj
│ └──►┌─ годом obl
│ ┌─────└► лидера nmod
│ │ ┌►┌─── запрещенной acl
│ │ │ │ ┌► в case
│ │ │ └►└─ России obl
│ ┌─└►└─┌─── Организации nmod
│ │ │ ┌► украинских amod
│ │ ┌─└►└─ националистов nmod
│ │ │ ┌► ( punct
│ │ └►┌─└─ ОУН parataxis
│ │ └──► ) punct
│ └──────►┌─ Степана appos
│ └► Бандеры flat:name
└──────────► . punct
''')
LEMMAS = {
'110-летия': '110-летие',
'Бандеры': 'бандера',
'Израиля': 'израиль',
'Львовской': 'львовский',
'Организации': 'организация',
'России': 'россия',
'Свое': 'свой',
'Степана': 'степан',
'Украине': 'украина',
'властей': 'власть',
'года': 'год',
'годом': 'год',
'декабря': 'декабрь',
'дня': 'день',
'запрещенной': 'запретить',
'лидера': 'лидер',
'националистов': 'националист',
'области': 'область',
'празднованием': 'празднование',
'признался': 'признаться',
'принял': 'принять',
'пришел': 'прийти',
'разместил': 'разместить',
'регионе': 'регион',
'решении': 'решение',
'родился': 'родиться',
'рождения': 'рождение',
'связи': 'связь',
'со': 'с',
'узнав': 'узнать',
'украинских': 'украинский',
'января': 'январь'
}
NORMALS = {
'Twitter': 'Twitter',
'Израиля': 'Израиль',
'Йоэль Лион': 'Йоэль Лион',
'Львовский областной совет': 'Львовский областной совет',
'Львовской области': 'Львовская область',
'ОУН': 'ОУН',
'Организации украинских националистов (ОУН)': 'Организация украинских '
'националистов (ОУН)',
'России': 'Россия',
'Степана Бандеры': 'Степан Бандера',
'Украине': 'Украина'
}
FACTS = {
'Йоэль Лион': {'first': 'Йоэль', 'last': 'Лион'},
'Степан Бандера': {'first': 'Степан', 'last': 'Бандера'}
}
def test_doc(segmenter, morph_vocab,
morph_tagger, syntax_parser, ner_tagger,
names_extractor, capsys):
doc = Doc(TEXT)
doc.segment(segmenter)
doc.tag_morph(morph_tagger)
doc.parse_syntax(syntax_parser)
doc.tag_ner(ner_tagger)
for span in doc.spans:
span.normalize(morph_vocab)
if span.type == PER:
span.extract_fact(names_extractor)
for token in doc.tokens:
token.lemmatize(morph_vocab)
doc.ner.print()
assert strip(capsys.readouterr().out) == NER
sent = doc.sents[0]
sent.morph.print()
assert strip(capsys.readouterr().out) == MORPH
sent.syntax.print()
assert strip(capsys.readouterr().out) == SYNTAX
lemmas = {
_.text: _.lemma
for _ in doc.tokens
if _.text.lower() != _.lemma
}
assert lemmas == LEMMAS
normals = {
_.text: _.normal
for _ in doc.spans
}
assert normals == NORMALS
facts = {
_.normal: _.fact.as_dict
for _ in doc.spans
if _.fact
}
assert facts == FACTS
import pytest
tests = [
[
'1 599 059, 38 Евро',
'1599059.38 EUR'
],
[
'2 134 472,44 рубля',
'2134472.44 RUB'
],
[
'420 долларов',
'420.00 USD'
],
[
'20 млн руб',
'20000000.00 RUB'],
[
'20 000 долларов',
'20000.00 USD'
],
[
'2,2 млн.руб.',
'2200000.00 RUB'
],
[
'2,20 млн.руб.',
'2200000.00 RUB'
],
[
'2,02 млн.руб.',
'2020000.00 RUB'
],
[
'20 тыс руб',
'20000.00 RUB'
],
[
'20 т. р.',
'20000.00 RUB'
],
[
'2 200 000 руб.',
'2200000.00 RUB'
],
[
'20.000 руб.',
'20000.00 RUB'
],
[
'20,000 руб',
'20000.00 RUB'
],
[
'20,00 руб',
'20.00 RUB'
],
[
'124 451 рубль 50 копеек',
'124451.50 RUB',
],
[
'881 913 (Восемьсот восемьдесят одна тысяча девятьсот тринадцать) руб. 98 коп.',
'881913.98 RUB'
]
]
@pytest.mark.parametrize('test', tests)
def test_extractor(money_extractor, test):
text, target = test
fact = money_extractor.find(text).fact
pred = '{fact.amount:0.2f} {fact.currency}'.format(fact=fact)
assert pred == target
import pytest
from natasha.obj import Name
tests = [
[
'Мустафа Джемилев',
Name('Мустафа', 'Джемилев')
],
[
'Егор Свиридов',
Name('Егор', 'Свиридов')
],
[
'Владимир Путин',
Name('Владимир', 'Путин')
],
[
'Плаксюк Саша',
Name('Саша', 'Плаксюк')
],
[
'О. Дерипаска',
Name('О', 'Дерипаска')
],
[
'Ищенко Е.П.',
Name('Е', 'Ищенко', 'П')
],
[
'Фёдор Иванович Шаляпин',
Name('Фёдор', 'Шаляпин', 'Иванович')
],
[
'Ипполит Матвеевич',
Name('Ипполит', 'Матвеевич')
],
[
'Янукович',
Name(None, 'Янукович')
],
[
'Авраам',
Name('Авраам')
],
[
'Гоша Куценко',
Name('Гоша', 'Куценко')
],
[
'Юрий Георгиевич Куценко',
Name('Юрий', 'Куценко', 'Георгиевич')
],
[
'Наталья Ищенко',
Name('Наталья', 'Ищенко')
],
[
'Наталья Сергеевна Ищенко',
Name('Наталья', 'Ищенко', 'Сергеевна')
],
[
'Монина Нина Гафуровна',
Name('Нина', 'Монина', 'Гафуровна')
],
[
'В. И. Ленин',
Name('В', 'Ленин', 'И'),
],
]
@pytest.mark.parametrize('test', tests)
def test_extractor(names_extractor, test):
text, target = test
pred = names_extractor.find(text).fact
assert pred == target
import pytest
from natasha.span import (
Span,
envelop_spans
)
tests = [
[
[(0, 1)],
[],
[]
],
[
[],
[(0, 1)],
[[]]
],
[
[(0, 1), (1, 2)],
[(1, 2)],
[[(1, 2)]]
],
[
[(0, 1), (1, 2)],
[(0, 1)],
[[(0, 1)]]
],
[
[(0, 1), (1, 2)],
[(0, 1), (1, 2)],
[[(0, 1)], [(1, 2)]]
],
]
def adapt_spans(spans):
for start, stop in spans:
yield Span(start, stop, type=None)
@pytest.mark.parametrize('test', tests)
def test_envelope_spans(test):
spans, envelopes, target = test
spans = list(adapt_spans(spans))
envelopes = list(adapt_spans(envelopes))
target = [
list(adapt_spans(group))
for group in target
]
pred = list(envelop_spans(spans, envelopes))
assert pred == target
from tqdm.notebook import tqdm as log_progress
from collections import defaultdict, Counter
from natasha import (
PER,
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
NamesExtractor,
Doc
)
from natasha.morph.lemma import normal_word
def adapt_spans(doc, spans):
from natasha.doc import DocSpan
for start, stop, type, normal in spans:
text = doc.text[start:stop]
yield DocSpan(start, stop, type, text)
def add_spans(doc, spans):
from natasha.span import index_spans, query_spans_index
doc.spans = list(adapt_spans(doc, spans))
# envelope tokens < spans < sents
index = index_spans(doc.tokens)
for span in doc.spans:
span.tokens = query_spans_index(index, span)
index = index_spans(doc.spans)
for sent in doc.sents:
sent.spans = query_spans_index(index, sent)

Sorry, the diff of this file is not supported yet

pymorphy2
razdel>=0.5.0
navec>=0.9.0
slovnet>=0.3.0
yargy>=0.14.0
ipymarkup>=0.8.0