Изменение инструмента отображения аннотаций для поддержки перекрывающихся объектов

#python #html #spacy

Вопрос:

Я работаю над инструментом для визуализации совпадений NER и пытаюсь устранить совпадения. Я использую displacy SpaCy в качестве шаблона, и моя идея состояла в том, чтобы добавить функцию после displacy.render() того, как она изменит HTML-строку в соответствии с желаемым результатом, но я несколько застрял. Вот мой процесс до сих пор:


Допустим, у меня есть некоторые text и некоторые entities (идентифицированные ранее каким-либо методом):

 text = "This is an example sentence with some super secret (and some text in between) stuff"
entities = [{"start": 38, "end": 43, "label": "SUPER"},
            {"start": 38, "end": 50, "label": "SUPER SECRET"},
            {"start": 78, "end": 83, "label": "STUFF"}]
 

Мы можем визуализировать их в формате HTML вручную, используя display:

 from spacy import displacy

example = [{"text": text,
            "ents": entities,
            "title": None}]
html = displacy.render(example, style="ent", manual=True)
 

который возвращает

 <div class="entities" style="line-height: 2.5; direction: ltr">This is an example sentence with some
<mark class="entity" style="background: #ddd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
    super
    <span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SUPER</span>
</mark>

<mark class="entity" style="background: #ddd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
    super secret
    <span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SUPER SECRET</span>
</mark>
 (and some text in between)
<mark class="entity" style="background: #ddd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
    stuff
    <span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">STUFF</span>
</mark>
</div>
 

что выглядит примерно так: текущий html


Проблема здесь в том, что «супер» находится отдельно <mark> от «суперсекрета». В идеале я бы хотел иметь что-то подобное:

 <div class="entities" style="line-height: 2.5; direction: ltr">This is an example sentence with
<mark class="entity" style="background: #ddd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
    <mark class="entity" style="background: #add; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
        super
        <span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SUPER</span>
    </mark>
    secret
    <span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SUPER SECRET</span>
</mark>
 (and some text in between)
<mark class="entity" style="background: #ddd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
    stuff
    <span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">STUFF</span>
</mark>
</div>
 

что выглядит примерно так: желаемый html

So my idea was to write an html_transform(starting_html: str) -> str function that resolves overlapping entities and returns something like the bottom image. After writing some psuedocode though, I realised that this might be a too complicated approach and I’m still stuck with it. Does anyone know of better ways of approaching this? Any pointers would be appreciated!