Python 处理文件的性能优化

2014-08-03 23:00:47 +08:00
 wwttc
现在有一个list包含有1500个topic,另外一个文件包含一亿个微博数据,现在我想统计,1500个topic中每个topic分别有多少条微博包含它们,我写的代码如下,但是运行起来需要非常久的时间,有什么办法可以优化吗?

f = file("largefile.txt")
for line in f:
try:
tweet_time = line.split(',',3)[2].split()[0]
tweet = line.split(',',3)[-1]
for topic in topics:
topic_items = topic.split()
isContain = True
for item in topic_items:
if item not in tweet:
isContain = False
break
if isContain:
pass
except:
continue
f.close()
8842 次点击
所在节点    问与答
44 条回复
answeror
2014-08-04 15:16:56 +08:00
抱歉, #40 的时间复杂度写错了, 应该是O(n+m+z), 其中n是所有微博的字符总数, m是所有topic的字符总数, z是topic item命中次数.
azuginnen
2014-08-04 16:33:59 +08:00
这个问题我在曹政博客上看到过相似的,后来一搜,知乎上貌似有人给过一些思路。

==============================
曹政@caoz 的开发工程师招聘问题三:如何实现一个快速有效的,基于自定义词典精确匹配的分词系统?修改

一个典型问题,目前政府有屏蔽词表,每个网站都要遵守,发帖的时候会自动替换屏蔽词;另一个场景是诸如新浪新闻等媒体往往有商业词表,发新闻的时候会自动建立关键词铆接。这个相当于一个简单的基于词典的分词系统,下面的问题就是,如何实现一个快速有效的,基于自定义词典精确匹配的分词系统,一是要满足每天几万篇,几十万篇文章发布的要求;另一个必须的要求是,当词库倍增扩展时(比如10万词),效率的影响不允许是线性降低的。

answer1
==============================
这个有很多办法,其实跟分词不一样,就是一个字符串匹配问题。
方法1,双哈希:
有2个哈希表,第一个是缩小范围的判定哈希,第二个是不同字数屏蔽词的哈希表。
每当我们读到一个字,就到第一个表里取一下,可以得到以这个字开头的屏蔽词的长度分别有哪些,比如(2,3,5)。然后分别从这个字开始,分2,3,5个字的词,去查第二个哈希表,查到了则返回危险,否则继续判定下一个字。
复杂度:O(L*n),L为以每个字开头的词长度的平均个数,n为输入流长度。(真绕)

方法2,有限自动机:
有限自动机说白了就是手工展开的正则表达式,把词表综合成一个巨大的有限自动机,每输入一个字就到自动机里查表,跳转状态,到匹配状态则为危险句子,到结束符则不危险。
时间复杂度:O(n)
空间复杂度:不可估计,可能会很大
方法3,一些其它字符串匹配算法的变形:
具体没细想,类似kmp,rk,bm的变形或许也能解决这个问题。


总之,最笨的方法是前向最大匹配,复杂度O(m*max(L)),其中max(L)为最长屏蔽词的长度。
一个好的匹配算法可以减少L的长度,检测并跳过没必要的计算。


answer2
==============================

由于敏感词范围有限,可以按字节将所有词分成N段。每字节共256种可能,维护一棵trie树,节点为256个指针。每个指针标识一字节,(比如第3个指针表示0x03这个字符)。所以一个M个字节的词被切成M份,为trie树中从根节点到第M层的一条链路。将所有待过滤词全部输入后,就能形成一棵查询trie树。其中敏感词对应的路径为通的,并且最后一个是页节点(比如abcde是敏感词,那么第5层的e对应的指针就是空的)。如果非敏感词,则无此通路或者有此通路但还有后续节点。

比如abcde是敏感词,abcdf不是
abcdf***是一个待过滤的字符串,在前四个字节匹配后,在第四层的f对应那个指针就为空。匹配失败
abc对应的c节点还有后续节点,那么abc也就不在敏感词中

在查询时,通过将文章按字节在trie树中进行查找,如果能一直有路径到叶节点,那么目前这条从根节点到叶节点的路径就是第三词。

如果不是,很明显我们需要将文章向后移一位再做对比。这样其实复杂度非常高。

解决这个问题可以引入KMP算法并将其扩展,将每个节点在匹配失败后,文章的下一字节应该到哪个节点重新开始匹配计算出来,将文章下一字节直接与这个节点进行匹配,这样每次文章只需要遍历一次。复杂度为O(n),n为文章长度。

至于空间复杂度,最坏的情况下,当所有敏感词链路没有交集时,由于使用trie树结构,整个数据会膨胀(256+256)*4 = 2048 倍,两个256分别表示一个节点中的两套指针(子节点指针与KMP需要的指针),4表示一个字节变成了4个字节(如果是32位机器的话)。 当然,如果将所有节点在一个数组下分配的话,就不需要存指针了,存数组位置即可。数据小数组可以2^16长,这时就变成2字节了,数据大2^32个节点差不多也够了吧。而且在64位机下也一样。

所以10w级别的词,按每个词平均10字节算,一共1M,最坏情况下需要2G内存实现。


http://www.zhihu.com/question/19918081
gejigeji
2014-08-04 16:49:32 +08:00
主要是 大文件切成小文件,整成多线程,还有就是readlines(size),size看内存大小 吧~
clino
2014-08-06 18:29:04 +08:00
楼主后来结果如何?什么方法有效?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/125941

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX