Во-первых, некоторые примерные данные:
from typing import List
from autocorrect import spell
import pandas as pd
from sklearn.datasets import fetch_20newsgroups
data_train: List[str] = fetch_20newsgroups(
subset='train',
categories=['alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space'],
shuffle=True,
random_state=444
).data
df = pd.DataFrame({"train": data_train})
Размер корпуса:
>>> df.shape
(2034, 1)
Средняя длина документа в символах:
>>> df["train"].str.len().mean()
1956.4896755162242
Первое наблюдение: spell()
(я никогда не использовал autocorrect
) работает очень медленно. Только на один документ уходит 7,77 с!
>>> first_doc = df.iat[0, 0]
>>> len(first_doc.split())
547
>>> first_doc[:100]
'From: [email protected] (David B. Mckissock)\nSubject: Gibbons Outlines SSF Redesign Guida'
>>> %time " ".join((spell(i) for i in first_doc.split()))
CPU times: user 7.77 s, sys: 159 ms, total: 7.93 s
Wall time: 7.93 s
Так что эта функция, а не выбор между векторизованным методом Pandas или .apply()
, вероятно, является вашим узким местом. Расчет обратной стороны конверта, учитывая, что длина этого документа составляет примерно 1/3 от среднего, дает общее время непараллельного расчета 7,93 * 3 * 2034 == 48 388 секунд. Не красиво.
С этой целью рассмотрите возможность параллелизации. Это задача с высокой степенью распараллеливания: примените простой вызываемый объект, привязанный к процессору, для набора документов. concurrent.futures
имеет для этого простой API. На этом этапе вы можете взять структуру данных из Pandas во что-то легкое, например список или кортеж.
Пример:
>>> corpus = df["train"].tolist() # or just data_train from above...
>>> import concurrent.futures
>>> import os
>>> os.cpu_count()
24
>>> with concurrent.futures.ProcessPoolExecutor() as executor:
... corrected = executor.map(lambda doc: " ".join((spell(i) for i in doc)), corpus)
person
Brad Solomon
schedule
09.08.2019