#python #xml #xpath
#python #xml #xpath
Вопрос:
Я хотел получить PMID, и для каждого PMID список других из списка авторов, для каждого PMID я мог бы получить список авторов, аналогично для всех других PMId я мог бы получить список авторов
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE PubmedArticleSet SYSTEM "http://dtd.nlm.nih.gov/ncbi/pubmed/out/pubmed_190101.dtd">
<PubmedArticleSet>
<PubmedArticle>
<MedlineCitation Status="MEDLINE" Owner="NLM">
<PMID Version="1">2844048</PMID>
<DateCompleted>
<Year>1988</Year>
<Month>10</Month>
<Day>26</Day>
</DateCompleted>
<DateRevised>
<Year>2010</Year>
<Month>11</Month>
<Day>18</Day>
</DateRevised>
<AuthorList CompleteYN="Y">
<Author ValidYN="Y">
<LastName>Guarner</LastName>
<ForeName>J</ForeName>
<Initials>J</Initials>
<AffiliationInfo>
<Affiliation>Department of Pathology and Laboratory Medicine, Emory University Hospital, Atlanta, Georgia.</Affiliation>
</AffiliationInfo>
</Author>
<Author ValidYN="Y">
<LastName>Cohen</LastName>
<ForeName>C</ForeName>
<Initials>C</Initials>
</Author>
</AuthorList>
</MedlineCitation>
Я могу извлекать данные по отдельности, но не понимаю, как их сгруппировать из-за структуры тегов.
tree = ET.parse('x.xml')
root = tree.getroot()
pid =[]
for pmid in root.iter('PMID'):
pid.append(pmid.text)
lastname=[]
for id in root.findall("./PubmedArticle/MedlineCitation/Article/AuthorList"):
for ln in id.findall("./Author/LastName"):
lastname.append(ln.text)
forename=[]
for id in root.findall("./PubmedArticle/MedlineCitation/Article/AuthorList"):
for fn in id.findall("./Author/ForeName"):
forename.append(fn.text)
initialname=[]
for id in root.findall("./PubmedArticle/MedlineCitation/Article/AuthorList"):
for i in id.findall("./Author/Initials"):
initialname.append(i.text)
Ожидаемый результат
PMID AUTHORS
2844048 'Guarner J J', 'Cohen C C'
Пожалуйста, предложите возможный способ решения проблемы, ожидаемый результат будет содержать большее количество строк, заранее спасибо,
Комментарии:
1. Можете ли вы привести пример ожидаемого результата?
2. пожалуйста, проверьте, я обновил вопрос.
3. Ваш вопрос помечен как XSLT. Ваш код не является XSLT. На какой ответ вы надеетесь?
4. Я изменил его.
Ответ №1:
Я думаю, что у меня получилось, хотя это заняло некоторое время. Чтобы сделать это упражнение интересным, я внес некоторые изменения.
Во-первых, xml-код в вашем вопросе недопустим; вы можете проверить это, например, здесь.
Итак, сначала я исправил xml. Кроме того, я превратил его в PubmedArticleSet, так что в нем есть 2 статьи, у первой статьи 3 автора, а у второй — два (фиктивная информация, очевидно), просто чтобы убедиться, что код захватывает их все. Чтобы сделать это несколько проще, я удалил некоторую нерелевантную (для этого упражнения) информацию, такую как принадлежность.
Итак, вот к чему это нас приводит. Сначала модифицированный xml:
source = """
<PubmedArticleSet>
<PubmedArticle>
<MedlineCitation Status="MEDLINE" Owner="NLM">
<PMID Version="1">2844048</PMID>
<AuthorList CompleteYN="Y">
<Author ValidYN="Y">
<LastName>Guarner</LastName>
<ForeName>J</ForeName>
<Initials>J</Initials>
</Author>
<Author ValidYN="Y">
<LastName>Cohen</LastName>
<ForeName>C</ForeName>
<Initials>C</Initials>
</Author>
<Author ValidYN="Y">
<LastName>Mushi</LastName>
<ForeName>E</ForeName>
<Initials>F</Initials>
</Author>
</AuthorList>
</MedlineCitation>
</PubmedArticle>
<PubmedArticle>
<MedlineCitation Status="MEDLINE" Owner="NLM">
<PMID Version="1">123456</PMID>
<AuthorList CompleteYN="Y">
<Author ValidYN="Y">
<LastName>Smith</LastName>
<ForeName>C</ForeName>
<Initials>C</Initials>
</Author>
<Author ValidYN="Y">
<LastName>Jones</LastName>
<ForeName>E</ForeName>
<Initials>F</Initials>
</Author>
</AuthorList>
</MedlineCitation>
</PubmedArticle>
"""
Далее импортируйте то, что необходимо импортировать:
from lxml import etree
import pandas as pd
Далее код:
doc = etree.fromstring(source)
art_loc = '..//*/PubmedArticle' #this is the path to all the articles
#count the number of articles in the article set - that number is a float has to be converted to integer before use:
num_arts = int(doc.xpath(f'count({art_loc})')) # or could use len(doc.xpath(f'({art_loc})'))
grand_inf = [] #this list will hold the accumulated information at the end
for art in range(1,num_arts 1): #can't do range(num_arts) because of the different ways python and Pubmed count
loc_path = (f'{art_loc}[{art}]/*/') #locate the path to each article
#grab the article id:
id_path = loc_path 'PMID'
pmid = doc.xpath(id_path)[0].text
art_inf = [] #this list holds the information for each article
art_inf.append(pmid)
art_path = loc_path '/Author' #locate the path to the author group
#determine the number of authors for this article; again, it's a float which needs to converted to integer
num_auths = int(doc.xpath(f'count({art_path})')) #again: could use len(doc.xpath(f'({art_path})'))
auth_inf = [] #this will hold the full name of each of the authors
for auth in range(1,num_auths 1):
auth_path = (f'{art_path}[{auth}]') #locate the path to each author
LastName = doc.xpath((f'{auth_path}/LastName'))[0].text
FirstName = doc.xpath((f'{auth_path}/ForeName'))[0].text
Middle = doc.xpath((f'{auth_path}/Initials'))[0].text
full_name = LastName ' ' FirstName ' ' Middle
auth_inf.append(full_name)
art_inf.append(auth_inf)
grand_inf.append(art_inf)
Наконец, загрузите эту информацию во фрейм данных:
df=pd.DataFrame(grand_inf,columns=['PMID','Author(s)'])
df
Вывод:
PMID Author(s)
0 2844048 [Guarner J J, Cohen C C, Mushi E F]
1 123456 [Smith C C, Jones E F]
И теперь мы можем отдохнуть…
Комментарии:
1. num_arts = len(doc.xpath(f'({art_loc})’))), lines выдает недопустимую синтаксическую ошибку, я думаю, из-за f’, это хорошо работает с Jupyter, но в терминале с Python 2.7 выдает ошибку, есть предложения?
2. Боюсь, ссылка на «Python 2.7 никогда не будет поддерживать f-строки»…
3. Да, я прочитал документацию. Есть ли альтернатива для python 2.7?
4. Я не тестировал его, но я полагаю, что вы можете заменить
(f'({art_loc}))
на('{0}'.format(art_loc))
.5. Спасибо, но мне нужно заменить весь строковый код f на что-то в Python 3.4.2, мы не можем получить более новую версию на сервере. Пожалуйста, предложите.
Ответ №2:
Модель данных XPath 1.0 определена в спецификации:
3.3 Наборы узлов
3.4 Логические значения
3.5 Числа
3.6 Строки
Набор узлов — это правильные наборы: дедуплицированные и неупорядоченные. Вам нужна последовательность, упорядоченный список данных (например, упорядоченный список набора узлов). Этот тип данных является частью XPath 2.0 и последующих версий.
Для группировки в XPath 1.0 в качестве встроенного языка вы выбираете «первый в своем роде», а затем используете основной язык для обхода документа, получая сгруппированные элементы, даже с другим выражением XPath. Именно так это делается в самом XSLT.