Я пытаюсь написать сценарий, который получает исследовательскую статью с веб-сайта, вызывая их API, а затем просматривая его по предложениям с некоторыми условиями.
Статья доступна в формате XML. Я напрямую использую nltk.sent_tokenize()
чтобы разбить соответствующую часть XML-документа на список предложений, а затем выполнить поиск «болезни» (количество болезней в моем фреймворке 12000) в каждом предложении с использованием регулярного выражения, и если совпадение найдено, я ищу «биомаркеры» (этот фрейм данных имеет 20000 строк, а тип данных строки относится к списку типов, где каждый список имеет в среднем 2 значения, т.е. существует более 40000 значений, которые должны быть сопоставлены) в том же предложении. И, наконец, результат сохраняется в базе данных, если болезнь и биомаркер встречаются в одном предложении.
В худшем случае шаги, предпринятые для выполнения задачи, будут 900 000(документы, которые необходимо просмотреть) *15(количество предложений в каждой статье) *12.000(номер болезни, по которой необходимо провести поиск) *40 000(номер маркера, который нужно искать).
На данный момент код анализирует 60-70 статей за час, что слишком медленно.
Ниже приведен код, который я пробовал. Я ищу существующие узкие места в своем коде. Мы будем очень благодарны за любые предложения по оптимизации кода.
import timeit
import mysql.connector
from urllib.request import urlopen
import urllib.request
import xml.dom.minidom
import xml.etree.ElementTree as ET
import requests
import xml
import lxml.etree
from lxml import etree
import re
import math
import nltk
import pandas as pd
start = timeit.default_timer()
print("start_time:", start)
mydb = mysql.connector.connect(host="localhost",user="root",password="",database="biomarker")
mycursor = mydb.cursor()
mycursor.execute("DROP TABLE IF EXISTS papers_sentence_com_ppr")
mycursor.execute("create table papers_sentence_com_ppr (id INT AUTO_INCREMENT PRIMARY KEY, paper_id VARCHAR(255), marker VARCHAR(255),marker_id VARCHAR(255), disease_name VARCHAR(255),AUTHORS TEXT, sentence TEXT)")
mycursor.execute("ALTER TABLE biomarker.papers_sentence_com_ppr CONVERT TO CHARACTER SET utf8")
#mycursor.execute("create table papers (id INT AUTO_INCREMENT PRIMARY KEY, paper_id VARCHAR(255), count VARCHAR(255),disease_name VARCHAR(255))")
df1 = pd.read_sql_query("select name from biomarker.disease2", mydb)
df = pd.read_sql_query("select * from biomarker.table_35", mydb)
print(df1)
biomarker_txt = df.at[2,'CA']
biomarker = biomarker_txt.split(" ")
print(len(biomarker))
#mycursor.execute("create table papers (id INT AUTO_INCREMENT PRIMARY KEY, paper_id VARCHAR(255), count VARCHAR(255),disease_name VARCHAR(255))")
urla="https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=(%22biomarker%22%20OR%20%22biomarkers%22%20OR%20%22biological%20marker%22%20OR%20%22biological%20markers%22)%20%20AND%20%20(LANG%3A%22eng%22%20OR%20LANG%3A%22en%22%20OR%20LANG%3A%22us%22)%20AND%20%20(HAS_ABSTRACT%3Ay)%20%20AND%20%20(SRC%3A%22PPR%22)&resultType=idlist&pageSize=1000&format=xml"
urlb='https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=(%22biomarker%22%20OR%20%22biomarkers%22%20OR%20%22biological%20marker%22%20OR%20%22biological%20markers%22)%20%20AND%20%20(LANG%3A%22eng%22%20OR%20LANG%3A%22en%22%20OR%20LANG%3A%22us%22)%20AND%20%20(HAS_ABSTRACT%3Ay)%20%20AND%20%20(SRC%3A%22PPR%22)&resultType=idlist&pageSize=1000&format=xml'
array1 = []
re1=requests.get(urla)
root = ET.fromstring(re1.content)
for hitCount in root.iter('hitCount'):
hit_count=int(hitCount.text)
result_value=hit_count
hit_count1=hit_count/1000
hit_count=math.ceil(hit_count1)
hit_count=10
counter1=0
counter3=0
y=0
i=1
for x in range(hit_count):
re1=requests.get(urla)
root1 = ET.fromstring(re1.content)
for id in root1.iter('id'):
id_text=id.text
array1.append(id.text)
for nextCursorMark in root1.iter('nextCursorMark'):
counter3=counter3+1
print(counter3)
urla=urlb
urla =urla+"&cursorMark="+nextCursorMark.text
for i in range(result_value):# will run 900.000 times for 900.000 papers
print("paper no:", i,)
paper_id=(array1[i]) #array1 contains list of paper ids
print("paper_id:", paper_id)
make_url="https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=ext_id:"+paper_id+'&resultType=core&format=xml'
re2=requests.get(make_url)
root2 = ET.fromstring(re2.content)
for abstractText in root2.iter('abstractText'):
abstract_text_without_tags= re.sub(r"<[^>]*>"," ",abstractText.text )#extract the relevant xml part
nltk_tokens = nltk.sent_tokenize(abstract_text_without_tags)#break the text into sentences
for text in range(len(nltk_tokens)):#depends on the length of text
for zz in range(len(df)):#df has 20.000 rows
biomarker_txt=((df.at[zz,'CA']))
biomarker = biomarker_txt.split(" ")# a cell could have more than value which are separated by an space
for tt in range(len(biomarker)):
if len(biomarker[tt])>2:
matches_for_marker = re.findall(rf"b{re.escape(biomarker[tt])}b", nltk_tokens[text])
if len(matches_for_marker)!=0:
for y in range(len(df1)):
disease_name=(df1.at[y,'name'])
regex_for_dis = rf"b{disease_name}b"
matches_for_dis= re.findall(regex_for_dis, nltk_tokens[text], re.IGNORECASE | re.MULTILINE)
#matches_for_dis = [re.findall(rf"b{(df1.at[y,'name'])}b", nltk_tokens[text], re.IGNORECASE | re.MULTILINE) for y in range(len(df1))]
if len(matches_for_dis)!=0:
for firstPublicationDate in root2.iter('firstPublicationDate'):
firstPublicationDate=firstPublicationDate.text
for authorString in root2.iter('authorString'):
counter1=counter1+1
mycursor.execute("insert into papers_sentence_com_ppr (id, paper_id,firstPublicationDate, marker,marker_id, disease_name, AUTHORS, sentence) values (%s,%s,%s, %s, %s,%s, %s,%s)", (counter1, paper_id,firstPublicationDate, biomarker[tt],(df.at[zz,'Entry']), (df1.at[y,'name']), authorString.text, nltk_tokens[text]))
#print("***********************************************************DATABASE_ENTRY*************************************************************n")
mydb.commit()
```
1 ответ
В настоящее время весь ваш код существует в одной глобальной куче, без функций, подмодулей или классов. Представьте некоторые из них.
Я вижу, что ваш пароль отображается как пустой для базы данных. Несколько вещей:
- Очень надеюсь что пароль не фактически пусто, и что вы вместо этого пропустили его для проверки. Пожалуйста, не используйте пустой пароль.
- Этот сценарий должен входить не как root, а как пользователь с ограниченными разрешениями.
- Сохраните пароль для входа в базу данных в другом месте, в каком-нибудь безопасном кошельке, хранилище кредитов и т. Д.
Оберните ваше соединение и объекты курсора в with
контекстные менеджеры.
Вам определенно не следует отбрасывать всю таблицу в этом скрипте. Во всяком случае, может тебе стоит быть truncate
заставить его удалить все свои данные; но определенно не бросай это.
У вас куча неиспользованного импорта; все эти:
from urllib.request import urlopen
import urllib.request
import xml.dom.minidom
import xml
import lxml.etree
from lxml import etree
нужно удалить.
Измените конструкцию URL так, чтобы вместо этого:
https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=(%22biomarker%22%20OR%20%22biomarkers%22%20OR%20%22biological%20marker%22%20OR%20%22biological%20markers%22)%20%20AND%20%20(LANG%3A%22eng%22%20OR%20LANG%3A%22en%22%20OR%20LANG%3A%22us%22)%20AND%20%20(HAS_ABSTRACT%3Ay)%20%20AND%20%20(SRC%3A%22PPR%22)&resultType=idlist&pageSize=1000&format=xml
у вас есть четко определенный метод, который принимает сеанс, формирует словарь параметров, проверяет ответ и анализирует ET:
def get_ebi(session: Session, **kwargs: str) -> ET.ElementTree:
query = (
'("biomarker" OR "biomarkers" OR "biological marker" OR "biological markers") '
'AND (LANG:"eng" OR LANG:"en" OR LANG:"us") '
'AND (HAS_ABSTRACT:y) '
'AND (SRC:"PPR")'
)
with session.get(
'https://www.ebi.ac.uk/europepmc/webservices/rest/search',
params={
'format': 'xml',
'resultType': 'idlist',
'pageSize': 1_000,
'query': query,
**kwargs,
},
stream=True,
) as response:
response.raise_for_status()
root = ET.parse(response.raw)
return root
with Session() as sess:
root = get_ebi(sess)
for hitCount in root.iter('hitCount'):
hit_count = int(hitCount.text)
result_value = hit_count
hit_count1 = hit_count / 1_000
hit_count = math.ceil(hit_count1)
Кроме того, ваш код наполнен вещами, которые просто не имеют смысла. Вы перезаписываете переменные:
hit_count=math.ceil(hit_count1)
hit_count=10
У вас есть загадочные, непонятные имена переменных:
array1 = []
counter1=0
counter3=0
y=0
i=1
У вас есть переменные с немного разными именами, которые делают совершенно разные вещи:
for hitCount in root.iter('hitCount'):
hit_count=int(hitCount.text)
Этот последний пример также неправильно повторяет все hitCount
элементы, когда следует обращать внимание только на первое.
Встроенные модули тени:
for id in root1.iter('id'):
Вы называете переменные, которые прямо противоположны тому, что они содержат; text
на самом деле будет содержать целое число:
for text in range(len(nltk_tokens)):
Вы выполняете фиксацию в своем самом внутреннем цикле, когда это, вероятно, должно быть отложено до гораздо более позднего времени, возможно, до конца скрипта.
Вам следует попытаться решить эти проблемы, в частности организовать методы подпрограммы, а затем опубликовать новый вопрос.
Большое спасибо!! Я внесу указанные изменения и, надеюсь, процесс ускорится.
— sanjeev_gautam
@sanjeev_gautam Вы также можете опубликовать новый вопрос с обновленным кодом. Как только вы устраните очевидные недостатки текущего кода, кто-нибудь может найти хороший способ значительно его ускорить.
— Грейфер