natasha / yargy

Rule-based facts extraction for Russian language
MIT License
315 stars 40 forks source link

Получение (части) исходной строки из Match #59

Closed rominf closed 5 years ago

rominf commented 5 years ago

Не нашёл, как иначе выдрать строку из Match. Пользуюсь пока вот так:

def to_slice(span):
    return slice(span.start, span.stop)

matches = extractor(text)
match = matches[0]
result = text[to_slice(match.span)]

Разрешаю скопировать код to_slice в библиотеку :)

insolor commented 5 years ago

Можно так преобразовывать:

result = text[slice(*match.span)]

(примерно так же в этом ответе я делал https://ru.stackoverflow.com/a/889854/1365)

Как вариант можно добавить в класс span поля (property) start и stop, тогда span можно будет использовать в качестве индекса без преобразования в объект slice.

rominf commented 5 years ago

@insolor, спасибо, не обратил внимание, что это namedtuple. Но всё равно - не красиво.

insolor commented 5 years ago

@rominf насчет namedtuple не могу сказать (исходники не смотрел). Главное что iterable, а все что iterable можно передавать в функции через звездочку.

kuk commented 5 years ago

Спасибо за пуллреквест, мне кажется это лишнее, пользователю предлагается писать явно

match = matches[0]
start, stop = match.span
result = text[start:stop]
rominf commented 5 years ago

@alexanderkuk, это не PR, это issue) Но почему? Это же реально частое действие. Если можно сократить 3 строчки до 1 почему бы этого не сделать? Здесь я писал про Span.to_slice, но обсуждение перешло в Match.text. Можно же при извлечении сохранить текст в приватную переменную, а в property Match.text делать то, что описано.

kuk commented 5 years ago

Почему

Незначительное уменьшение код, я вижу сокращение одной строчки

match = matches[0]
start, stop = match.span
result = text[start:stop]

против

match = matches[0]
result = text[to_slice(match.span)]

Это же реально частое действие

На самом деле нет. Выделять сырые подстроки нужно обычно для отладки, для этого есть https://github.com/natasha/ipymarkup . Дальше обычно текст нужно структурировать и нормализовывать "Владимира Путина" -> first:владимир last:путин, сырые строки уже не используются

rominf commented 5 years ago

Ладно, я неправильно начал обсуждение. Забудьте про всё, что я писал раньше. Речь идёт не о Span, а о выделении текста (заголовок после этого поменяю).

Реальный пример. Мне надо находить названия организаций с веб-страниц (без ООО|ОАО|... и кавычек). Я захожу на главную, regexp-ом нахожу строку copyright, потом скармливаю её Наташе. Для того, чтобы никого не рекламировать и упростить код, скормлю другую строку. Spacy не умеет в русский язык пока, но хорошо справляется с недеструктивной токенизацией. Внизу сравнение.

text = 'ОАО "Ололо! Еда"'

from natasha import OrganisationExtractor
matches = extractor(text)
match = matches[0]
''.join(token.value for token in match.tokens[2:-1])  # 'Ололо!Еда'

import spacy
nlp = spacy.load('en')
doc = nlp(text)
str(doc[2:-1])  # 'Ололо! Еда'

Сравните простоту кода получения части исходной строки и то, что в Spacy пробелы на месте, а в Наташе - нет. Предлагаю в Наташе сделать так же.

PS: сейчас из-за деструктивной токенизации Наташи приходится выделять из строки весь Match целиком и допиливать regexp-ом. Это не круто!

dveselov commented 5 years ago

В вашем примере вы используете join по содержимому токенов - это не совсем то, что вам нужно, учитывая то, что внутри токенов хранится их оригинальное положение в тексте. Не вижу проблем использовать original_text[span_start:span_end], в вашем случае, если нужна недеструктивная токенизация.

dveselov commented 5 years ago

Собственно, про этот же способ чуть выше написал @alexanderkuk