-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathscispacy_tokenization.py
More file actions
executable file
·120 lines (90 loc) · 3.83 KB
/
Copy pathscispacy_tokenization.py
File metadata and controls
executable file
·120 lines (90 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# -*- coding: utf-8 -*-
import os
import time
import spacy
import argparse
import logging
from tqdm import tqdm
from pathlib import Path
from spacy.tokens import Doc
from typing import List, Generator
logging.basicConfig(
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%m/%d/%Y %H:%M:%S",
level=logging.INFO,
)
logger = logging.getLogger(__name__)
class AbstractsCorpus:
def __init__(self, data_dir: str):
self.data_dir = data_dir
def __iter__(self):
for fname in tqdm(os.listdir(self.data_dir), desc='Reading abstract texts from *.xml.gz.txt files ...'):
if fname.endswith('.xml.gz.txt'):
fname = Path(self.data_dir) / fname
yield from self.iter_abstracts_from_xml_gz_txt(fname)
@staticmethod
def iter_abstracts_from_xml_gz_txt(fname: str) -> Generator[str, None, None]:
"""Read an *.xml.gz.txt file and return abstract texts stored per line."""
with open(fname, encoding='utf-8', errors='ignore') as rf:
for abstract in rf:
abstract = abstract.strip()
if not abstract:
continue
yield abstract
def process_doc(doc: Doc) -> List[str]:
return [' '.join([tok.text for tok in sent]) for sent in doc.sents]
def main(args):
nlp = spacy.load(args.scispacy_model_name)
abstracts = iter(AbstractsCorpus(args.data_dir))
output_file = Path(args.data_dir) / 'medline_pubmed_2019_sents.txt'
idx = 0
sents = list()
num_sents = 0
total = 0
for fname in os.listdir(args.data_dir):
if fname.endswith('.xml.gz.txt'): total += 1
t = time.time()
# ---------------------------------------------------------------------------------------
# WARNING: depending on ``n_process`` and ``batch_size`` selection, multi-processing
# can be worse than sequential processing. One has to play around a bit with the
# system before it finds the right combination. There is no one size fits all!
#
# more here: https://spacy.io/usage/processing-pipelines#multiprocessing
# ---------------------------------------------------------------------------------------
for doc in tqdm(nlp.pipe(abstracts, n_process=args.n_process, batch_size=args.batch_size)):
if idx % 100000 == 0 and idx > 0:
speed = idx // ((time.time() - t) / 60)
logger.info(f'Processed {idx} abstracts from {total} pooled abstract files @ {speed} abstracts/min ...')
logger.info(f'Dumping batch of sentences!')
with open(output_file, 'a') as wf:
count = len(sents)
num_sents += count
for _ in range(count):
wf.write(sents.pop() + '\n') # clear out sents list
for sent in process_doc(doc):
sents.append(sent)
idx += 1
t = (time.time() - t) // 60
logger.info(f'Took {t} minutes and collected {num_sents} sentences !')
if __name__=="__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--data_dir", action="store", required=True, type=str,
help="Path to *.xml.gz files"
)
parser.add_argument(
"--scispacy_model_name", action="store", type=str, default="en_core_sci_lg",
help="ScispaCy model to use."
)
parser.add_argument(
"--n_process", action="store", type=int, default=4,
help="Number of processes to run in parallel with spaCy multi-processing."
)
parser.add_argument(
"--batch_size", action="store", type=int, default=256,
help="Batch size to use in combination with spaCy multi-processing."
)
args = parser.parse_args()
import pprint
pprint.pprint(vars(args))
main(args)