Преобразование аннотаций XML в формат BRAT
У меня есть аннотированный набор данных в формате XML: см. Пример ниже
Treatment of <annotation cui="C0267055">Erosive Esophagitis</annotation> in patients.
где отмеченные слова находятся в тегах XML, как показано. Мне нужно получить его в формате BRAT, как показано здесь: http://brat.nlplab.org/standoff.html
Я могу извлечь аннотации, используя регулярные выражения в Python, но я не уверен, как перевести их в правильный формат BRAT. Возможно, есть инструмент для этого?
0 ответов
Если кому-то все еще нужен ответ на этот вопрос, вот решение.
Скажем, файл XML sample.xml
имеет следующую структуру:
<root>
<p n='1'>Hi, my name is <fname>Mickey</fname> <lname>Mouse</lname>, and what about yourself?</p>
<p n='2'>Nice meeting you, <fname>Mickey</fname>! I am <fname>Minnie</lname>!</p>
</root>
Вот решение Python:
# leave empty if there are no tags that should not be interpreted as named entities; or add more
ignoretags = ['root', 'p']
# dictionary, in case some named entities have to be mapped; or just a list of tags that represent NEs
replacetags = {
"fname": "PERS",
"lname": "PERS"
}
# read content
content = open('sample.xml', encoding='utf-8').read()
# output files for BRAT: txt and annotations
f_txt = open('sample.txt', 'w')
f_ann = open('sample.ann', 'w')
# from txt file remove NE tags
clean_content = content
for replacetag in replacetags:
clean_content = clean_content.replace('<{}>'.format(replacetag), '')
clean_content = clean_content.replace('</{}>'.format(replacetag), '')
# write content to file
f_txt.write(clean_content)
# char by char
n = len(content)
i = - 1
# token id
tid = 0
# other - for output
start = -1
end = - 1
token = None
tag = None
# let's start parsing! character by character
skipped_chars = 0
number_of_tags = 0
token_buffer = ''
while i < n - 1:
i += 1
c = content[i]
# beginning of an entity
if c == '<':
# probably the most important part: always track the count of skipped characters
start = i - skipped_chars
# get name of the entity
tag_buffer = ''
i += 1
while content[i] != '>':
tag_buffer += content[i]
i += 1
tag = tag_buffer
# skip tags that are not NEs
if tag not in replacetags:
continue
# get entity itself
ent_buffer = ''
i += 1
while content[i] != '<':
ent_buffer += content[i]
i += 1
token = ent_buffer
# determine positions
end = start + len(token)
number_of_tags += 1
# <fname></fname> i.e. 1 + len('fname') + 1 + 1 + 1 + len('fname') + 1
skipped_chars += 1 + len(tag) + 1 + 1 + 1 + len(tag) + 1
tid += 1
# write annotation
f_ann.write('T{}\t{} {} {}\t{}\n'.format(tid, replacetags[tag], start, end, token))
# navigate to the end of the entity span, e.g. go behind <fname>...</fname>
i += 1 + len(tag) + 1
Содержание sample.txt
<root>
<p n='1'>Hi, my name is Mickey Mouse, and what about yourself?</p>
<p n='2'>Nice meeting you, Mickey! I am Minnie!</p>
</root>
Содержание sample.ann
:
T1 PERS 31 37 Mickey
T2 PERS 38 43 Mouse
T3 PERS 101 107 Mickey
T4 PERS 114 120 Minnie
И визуально в BRAT:
В случае атрибутов потребуется небольшая настройка (я добавил еще один ключ 'att' в replacetags
словарь, т.е. пара будет тогда "fname": {"tag": "PERS", "att": "value of attribute"}
а затем будет написана дополнительная строка в случае тега, у которого есть атрибут.
Надеюсь, кто-то сочтет это полезным!