【问题标题】:Speed up removing stopwords from huge csv-file加快从巨大的 csv 文件中删除停用词的速度
【发布时间】:2019-01-21 05:07:56
【问题描述】:

有没有更好(更快)的方法从 csv 文件中删除停用词?

这是简单的代码,一个多小时后我还在等待结果(所以我什至不知道它是否真的有效):

import nltk
from nltk.corpus import stopwords
import csv
import codecs

f = codecs.open("agenericcsvfile.csv","r","utf-8")
readit = f.read()
f.close()

filtered = [w for w in readit if not w in stopwords.words('english')]

csv 文件有 50.000 行,总共约 1500 万字。为什么需要这么长时间?可悲的是,这只是一个子语料库。我将不得不使用超过 100 万行和超过 3 亿字来执行此操作。那么有没有办法加快速度呢?还是更优雅的代码?

CSV 文件示例:

1 text,sentiment
2 Loosely based on The Decameron, Jeff Baena's subversive film takes us behind the walls of a 13th century convent and squarely in the midst of a trio of lustful sisters, Alessandra (Alison Brie), Fernanda (Aubrey Plaza), and Ginerva (Kate Micucci) who are "beguiled" by a new handyman, Massetto (Dave Franco). He is posing as a deaf [...] and it is coming undone from all of these farcical complications.,3
3 One might recommend this film to the most liberally-minded of individuals, but even that is questionable as [...] But if you are one of the ribald loving few, who likes their raunchy hi-jinks with a satirical sting, this is your kinda movie. For me, the satire was lost.,5
4 [...]
[...]
50.000 The movie is [...] tht is what I ahve to say.,9

所需的输出将是没有停用词的相同 csv 文件。

【问题讨论】:

  • 这是哪个 Python 版本 - 可以添加适当的标签吗?
  • 另外,请添加一个有意义的输入 CSV 样本和该样本所需的输出。
  • 顺便说一句,CSV中的字符串不应该被引用吗?否则,如何区分文本中的,, 分隔文本和情感?此外,readit 似乎只是一个包含文件中所有字符的字符串,而不是单词列表。 (您导入,但从不使用 csv 模块。)
  • @tobias_k 我试过了,但它会是一个没有换行符的字符串?有没有办法正确地做到这一点?

标签: python python-3.x csv stop-words


【解决方案1】:

NLTK 返回的停用词似乎是list,因此具有 O(n) 查找。先把列表转换成set,这样会快很多。

>>> some_word = "aren't"
>>> stop = stopwords.words('english')
>>> type(stop)
list
>>> %timeit some_word in stop
1000000 loops, best of 3: 1.3 µs per loop

>>> stop = set(stopwords.words('english'))
>>> %timeit some_word in stop
10000000 loops, best of 3: 43.8 ns per loop

但是,虽然这应该可以解决性能问题,但您的代码似乎并没有按照您的预期做。 readit 是包含整个文件内容的单个字符串,因此您正在迭代字符而不是单词。您导入了 csv 模块,但您从不使用它。此外,csv 文件中的字符串应该被引用,否则它将在 all , 处拆分,而不仅仅是在最后一个。如果您无法更改 csv 文件,则使用str.rsplit 可能会更容易。

texts = [line.rsplit(",", 1)[0] for line in readit.splitlines()]
filtered = [[w for w in text.split() if w.lower() not in stopwords_set]
            for text in texts]

【讨论】:

    【解决方案2】:

    第一个明显的优化是 1/ 避免在每次迭代中调用 stopwords.words() 和 2/ 使其成为 setset 查找为 O(1),其中 list 查找为 O(N)) :

    words = set(stopwords.words("english"))
    filtered = [w for w in readit if not w in words]
    

    但这不会产生预期的结果,因为readit 是一个字符串,所以您实际上是在迭代单个字符,而不是单词。您需要先标记您的字符串,[如此处所述][1]:

    from nltk.tokenize import word_tokenize
    readit = word_tokenize(readit)
    # now readit is a proper list of words...
    filtered = [w for w in readit if not w in words]
    

    但是现在您已经丢失了所有 csv 换行符,因此您无法正确重建它...如果您的 csv 中有任何引用,您可能也会遇到引用问题。因此,实际上您可能希望使用csv.reader 正确解析您的源代码,并逐个字段逐行清理您的数据,这当然会增加相当多的开销。好吧,如果您的目标是重建没有停用词的 csv,那就是(否则您可能不太在意)。

    Anwyay:如果你有一个非常庞大的语料库需要清理并且需要性能,那么下一步就是真正的并行化:将源数据分成几部分,将每个部分发送到不同的进程(每个处理器/内核一个是一个好的开始) ,可能分布在多台计算机上,并收集结果。这种模式被称为“map reduce”,它们已经是几个 Python 实现了。

    【讨论】:

    • 呃,我的代码的缺陷比想象的要多得多。我将不得不重新考虑整个事情。我不瘦我明白如何不丢失 csv 分隔符。你能指出我正确的方向吗?
    • @TAN-C-F-OK 抱歉,我在准备好之前不小心提交了答案。您实际上不会丢失标点符号(我刚刚使用 nltk 进行了测试),但 csv 格式可能很棘手,并且一次标记整个数据可能不起作用(至少不是以允许重建 csv 的方式)。参考我编辑的答案了解更多详情。
    • 在语料库是一个巨大的 csv 文件之前删除停用词会更好/更容易/更快吗?我得到了单个文本文件中的每一行(ergo 50K txt 文件)。问题是,我没有找到一种方法来处理所有 txt 文件(一次只有一个文件)
    • 处理大量小文件确实可以避免 csv 格式问题。它还使并行化变得更加容易。您所要做的就是用一个参数替换硬编码的文件路径,然后启动N个并行进程来处理您的文件(这部分可以写成另一个脚本)。
    猜你喜欢
    • 2020-03-15
    • 1970-01-01
    • 2015-01-20
    • 2011-08-30
    • 2019-10-01
    • 2019-11-09
    • 2014-06-22
    • 2011-05-03
    • 1970-01-01
    相关资源
    最近更新 更多