如何加快数据框中单词列表的单词移除速度?

1 投票
2 回答
68 浏览
提问于 2025-04-14 18:18

我正在尝试从一个中等大小的 pandas 数据框中去掉不在字典里的单词,这个数据框大约有 18,000 行,但我的方法非常慢。基本上,我尝试了列表推导式,并把它应用到整个数据框上。这样做是可以的,但速度太慢了,我还没有成功地把这个过程向量化。请问我该怎么做呢?

而且,我的方法似乎会同时影响多个数据框,这不是我想要的结果。我该如何解决这个问题呢?

下面的代码展示了我采取的方法,只是数据量少得多:

import pandas as pd

df = pd.DataFrame({'Text': [['this', 'is', 'an', 'apple'], 
                            ['this', 'is', 'a', 'carrot'], 
                            ['this', 'is', 'a', 'steak']],
                   'Class': ['fruit', 'vegetable', 'meat']})

valid_words = ['apple', 'carrot', 'steak']

def dictwords(text):
  valid_text = [word for word in text if word in valid_words]
  return valid_text

clean = df

clean['Text'] = clean['Text'].apply(dictwords)

这个方法可以用,但对于我的实际数据来说太慢了。我的真实数据集中大约有 60,000 个独特的单词,包括有效和无效的,我只想保留大约 30,000 个。文本大约有 18,000 行。正如大家所预料的,使用 .apply() 进行这个过程会花费非常长的时间。

我尝试过使用 njit/jit 来进行并行处理,但效果不太好。对于这些数据,我可以尝试哪些向量化或并行处理的技术?有没有比列表推导式更好的方法呢?

另外,我发现当我把 dictwords() 应用到干净的数据集时,它似乎也同时应用到了 df 上。我不太明白为什么会这样,或者如何防止这种情况,所以如果能解释一下就太好了。在我测试过的所有 Jupyter Notebook 平台上似乎都有这个问题。

2 个回答

0

示例

生成60,000个独特的单词,30,000个目标单词,以及18,000行的数据框样本

import pandas as pd
import numpy as np

# 60k unique word ('0' ~ '59999')
words = list(map(str, range(0, 60000)))

# target 30k word ('0' ~ '29999')
valid_words = words[:30000]

# random dataframe 18k rows
np.random.seed(0)
val = np.random.choice(words, (18000, 4)).tolist()
df = pd.DataFrame({'Text': val})

数据框

        Text
0       [2732, 43567, 42613, 52416]  <-- not numeric, string(think like word)
1       [45891, 21243, 30403, 32103]
2       [41993, 57043, 20757, 55026]
... ...
17997   [6688, 22472, 36124, 56143]
17998   [55253, 29436, 4113, 22639]
17999   [1128, 12103, 39056, 28174]
18000 rows × 1 columns

代码

虽然这不是一个完全的向量化操作,但我们可以通过向量化的方式来创建一个数据框,并把不在valid_words里的值替换成NaN(缺失值)。这样做的速度会比你原来的方法快,如果你最后把结果聚合成一个列表的话。

out = (pd.DataFrame(df['Text'].tolist())[lambda x: x.isin(valid_words)]
       .apply(lambda x: list(x.dropna()),axis=1)
)

输出

0                      [2732]
1                     [21243]
2                     [20757]
                 ...         
17997           [6688, 22472]
17998    [29436, 4113, 22639]
17999    [1128, 12103, 28174]
Length: 18000, dtype: object

执行时间如下:

970 ms ± 50.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1

因为“Text”这一列里包含了列表,所以你可以用pandas的explode功能,把这些列表拆开,然后用isin函数来筛选出只包含你列表中单词的行。

如果每一行只包含你列表中的一个单词,你只需要前两行代码就可以了。但如果你想把单词放回列表里,或者某些行包含了你列表中的多个单词,那你就需要用到第三行代码。

clean = df.explode('Text')
clean = clean[clean['Text'].isin(valid_words)]
clean = clean.groupby(clean.index).agg({'Text': list, 'Class':'first'})

最终结果

Text            Class
[apple, carrot] fruit
[carrot]        vegetable
[steak]         meat

第一行中的胡萝卜是我在测试数据中添加的,用来测试如果某些行中有多个单词的情况。

撰写回答