将 Spacy Parser 应用于 Pandas DataFrame w/ Multiprocessing
Applying Spacy Parser to Pandas DataFrame w/ Multiprocessing
假设我有一个数据集,比如
iris = pd.DataFrame(sns.load_dataset('iris'))
我可以使用 Spacy
和 .apply
将字符串列解析为标记(我的真实数据集当然每个条目有 >1 word/token)
import spacy # (I have version 1.8.2)
nlp = spacy.load('en')
iris['species_parsed'] = iris['species'].apply(nlp)
结果:
sepal_length ... species species_parsed
0 1.4 ... setosa (setosa)
1 1.4 ... setosa (setosa)
2 1.3 ... setosa (setosa)
我还可以使用这个方便的多处理函数 (thanks to this blogpost) 在数据帧上并行执行大多数任意应用函数:
from multiprocessing import Pool, cpu_count
def parallelize_dataframe(df, func, num_partitions):
df_split = np.array_split(df, num_partitions)
pool = Pool(num_partitions)
df = pd.concat(pool.map(func, df_split))
pool.close()
pool.join()
return df
例如:
def my_func(df):
df['length_of_word'] = df['species'].apply(lambda x: len(x))
return df
num_cores = cpu_count()
iris = parallelize_dataframe(iris, my_func, num_cores)
结果:
sepal_length species length_of_word
0 5.1 setosa 6
1 4.9 setosa 6
2 4.7 setosa 6
...但出于某种原因,我无法通过这种方式将 Spacy 解析器应用于使用多处理的数据帧。
def add_parsed(df):
df['species_parsed'] = df['species'].apply(nlp)
return df
iris = parallelize_dataframe(iris, add_parsed, num_cores)
结果:
sepal_length species length_of_word species_parsed
0 5.1 setosa 6 ()
1 4.9 setosa 6 ()
2 4.7 setosa 6 ()
还有其他方法吗?我喜欢 NLP 的 Spacy,但我有很多文本数据,所以我想并行化一些处理功能,但 运行 进入这个问题。
Spacy 经过高度优化,可以为您进行多处理。因此,我认为最好的办法是将数据从 Dataframe 中取出并将其作为列表传递给 Spacy 管道,而不是尝试直接使用 .apply
。
然后您需要整理解析结果,并将其放回 Dataframe 中。
因此,在您的示例中,您可以使用如下内容:
tokens = []
lemma = []
pos = []
for doc in nlp.pipe(df['species'].astype('unicode').values, batch_size=50,
n_threads=3):
if doc.is_parsed:
tokens.append([n.text for n in doc])
lemma.append([n.lemma_ for n in doc])
pos.append([n.pos_ for n in doc])
else:
# We want to make sure that the lists of parsed results have the
# same number of entries of the original Dataframe, so add some blanks in case the parse fails
tokens.append(None)
lemma.append(None)
pos.append(None)
df['species_tokens'] = tokens
df['species_lemma'] = lemma
df['species_pos'] = pos
这种方法适用于小型数据集,但它会占用你的内存,所以如果你想处理大量文本就不是很好了。
假设我有一个数据集,比如
iris = pd.DataFrame(sns.load_dataset('iris'))
我可以使用 Spacy
和 .apply
将字符串列解析为标记(我的真实数据集当然每个条目有 >1 word/token)
import spacy # (I have version 1.8.2)
nlp = spacy.load('en')
iris['species_parsed'] = iris['species'].apply(nlp)
结果:
sepal_length ... species species_parsed
0 1.4 ... setosa (setosa)
1 1.4 ... setosa (setosa)
2 1.3 ... setosa (setosa)
我还可以使用这个方便的多处理函数 (thanks to this blogpost) 在数据帧上并行执行大多数任意应用函数:
from multiprocessing import Pool, cpu_count
def parallelize_dataframe(df, func, num_partitions):
df_split = np.array_split(df, num_partitions)
pool = Pool(num_partitions)
df = pd.concat(pool.map(func, df_split))
pool.close()
pool.join()
return df
例如:
def my_func(df):
df['length_of_word'] = df['species'].apply(lambda x: len(x))
return df
num_cores = cpu_count()
iris = parallelize_dataframe(iris, my_func, num_cores)
结果:
sepal_length species length_of_word
0 5.1 setosa 6
1 4.9 setosa 6
2 4.7 setosa 6
...但出于某种原因,我无法通过这种方式将 Spacy 解析器应用于使用多处理的数据帧。
def add_parsed(df):
df['species_parsed'] = df['species'].apply(nlp)
return df
iris = parallelize_dataframe(iris, add_parsed, num_cores)
结果:
sepal_length species length_of_word species_parsed
0 5.1 setosa 6 ()
1 4.9 setosa 6 ()
2 4.7 setosa 6 ()
还有其他方法吗?我喜欢 NLP 的 Spacy,但我有很多文本数据,所以我想并行化一些处理功能,但 运行 进入这个问题。
Spacy 经过高度优化,可以为您进行多处理。因此,我认为最好的办法是将数据从 Dataframe 中取出并将其作为列表传递给 Spacy 管道,而不是尝试直接使用 .apply
。
然后您需要整理解析结果,并将其放回 Dataframe 中。
因此,在您的示例中,您可以使用如下内容:
tokens = []
lemma = []
pos = []
for doc in nlp.pipe(df['species'].astype('unicode').values, batch_size=50,
n_threads=3):
if doc.is_parsed:
tokens.append([n.text for n in doc])
lemma.append([n.lemma_ for n in doc])
pos.append([n.pos_ for n in doc])
else:
# We want to make sure that the lists of parsed results have the
# same number of entries of the original Dataframe, so add some blanks in case the parse fails
tokens.append(None)
lemma.append(None)
pos.append(None)
df['species_tokens'] = tokens
df['species_lemma'] = lemma
df['species_pos'] = pos
这种方法适用于小型数据集,但它会占用你的内存,所以如果你想处理大量文本就不是很好了。