使用Spacy解析器和多进程应用于Pandas DataFrame

35

假设我有一个数据集,像这样

iris = pd.DataFrame(sns.load_dataset('iris'))

我可以使用Spacy.apply将字符串列解析为标记(我的实际数据集每个条目当然有>1个单词/标记)

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)

我还可以使用这个方便的多进程函数(感谢这篇博客文章),以并行方式对数据框执行大多数任意应用函数:

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
例如:

for example:

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             ()

有没有其他方法可以做到这一点?我正在喜欢Spacy进行NLP,但我有很多文本数据,所以我想并行一些处理函数,但遇到了这个问题。

还有其他方法可以解决这个问题吗?我很喜欢使用Spacy进行自然语言处理,但是由于我的文本数据很多,因此我想要并行一些处理函数,但我遇到了这个问题。


返回结果:

还有其他方法可以解决这个问题吗?我很喜欢使用Spacy进行自然语言处理,但是由于我的文本数据很多,因此我想要并行一些处理函数,但我遇到了这个问题。

1个回答

43

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

这种方法对于小型数据集效果很好,但会消耗您的内存,因此如果您想处理大量文本,则不是很理想。


15
针对大型数据框架,推荐的方法是什么? - ℕʘʘḆḽḘ
1
如果您拥有一个顺序索引,我建议使用“enumerate”函数枚举管道并使用“loc”函数就地添加值。 - Mathew Savage
为了避免使用缓存内存,您可以每次将一行写入磁盘文件。在分析的后期阶段,您可以使用这些新创建的工件。 - mabounassif
2
pipe()n_threads 参数已于2019年被弃用n_process 可作为一个合理的替代品。 - Cold Fish
1
doc.is_parsed 属性已经在 spacy v3.0 中被弃用。文档建议使用 doc.has_annotation("DEP") 替代。 - scign

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接