Python:这是一种比较和排序字符串列表的低效方法吗?

时间:2017-08-22 16:53:25

标签: python performance nltk string-comparison cosine-similarity

我有两个字符串列表,A和B.对于A中的每个字符串,我想将它与B中的每个字符串进行比较,并选择最相似的匹配。我使用的比较函数是I found on this question的自定义余弦相似性度量。以下是它的工作原理:

import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer
nltk.download('punkt')

stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)

def stem_tokens(tokens):
    return [stemmer.stem(item) for item in tokens]

def normalize(text):
    return stem_tokens(nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))

vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')

def cosine_sim(text1, text2):
    tfidf = vectorizer.fit_transform([text1, text2])
    return ((tfidf * tfidf.T).A)[0,1]

我的问题是,如果我有一些长列表(500-1000项),执行开始需要五或十分钟。以下是使用虚拟文本的示例:

import requests    
url = 'https://gist.githubusercontent.com/WalkerHarrison/940c005aa23386a69282f373f6160221/raw/6537d999b9e39d62df3784d2d847d4a6b2602876/sample.txt'
sample = requests.get(url).text
A, B = sample[:int(len(sample)/2)], sample[int(len(sample)/2):]
A, B = list(map(''.join, zip(*[iter(A)]*100))), list(map(''.join, zip(*[iter(B)]*100)))

现在我有两个列表,每个列表有~500个字符串(每个字符100个字符),我计算相似之处并取得最重要的一个。这是通过从A中取一个字符串,迭代B,按cosine_sim得分排序,然后取最后一个元素,然后重复A中的所有元素来完成的:

matches = [(a, list(sorted([[b, cosine_sim(a, b)] 
                            for b in B], key=lambda x: x[1]))[-1])
           for a in A]

输出是匹配列表,其中每个项目包含两个字符串以及它们的计算相似度分数。最后一行需要7分钟才能运行。我想知道我的流程中效率低下是否会降低速度,或者是否计算得很多(500 * 500 = 250,000比较,加上最佳500次排序)?

1 个答案:

答案 0 :(得分:1)

最大的问题可能是你为每一对文件计算tfidf(这里的文件仅仅意味着你的文本单元 - 这可能是一条推文,一个句子,一篇科学论文或一本书)。此外,如果已经存在,您不应该制作自己的相似性度量。最后,sklearn有一个pairwise_distance例程,可以执行您想要的操作并进行优化。把这一切放在一起,这是一个示例脚本:

import requests
import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import pairwise_distances

url = 'https://gist.githubusercontent.com/WalkerHarrison/940c005aa23386a69282f373f6160221/raw/6537d999b9e39d62df3784d2d847d4a6b2602876/sample.txt'
sample = requests.get(url).text.split('\n\n') # I'm splitting the document by "paragraphs" since it is unclear what you actually want

stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)

def stem_tokens(tokens):
    return [stemmer.stem(item) for item in tokens]

def normalize(text):
    return stem_tokens(nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))

vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')


doc_vectors = vectorizer.fit_transform(sample)
distances = pairwise_distances(doc_vectors, metric='cosine')


row_idx = list(enumerate(distances.argmax(axis=1)))
sorted_pairs = sorted(row_idx, key= lambda x: distances[x[0], x[1]], reverse=True)

# most similar documents:

i1, i2 = sorted_pairs[0] # index of most similar pair

print(sample[i1])
print("=="*50)
print(sample[i2])

我的sample列表中有99个文档,这些文件在下载完成后立即运行。另外,输出:

  

艺术派对动物标本制作3虎月亮占据。手提包twee炸玉米饼   listicle,屠夫单一来源咖啡raclette gentrify生牛仔布   helvetica羽衣甘蓝筹码萨满威廉斯堡男子编织。戳normcore lomo   健康哥特背心kogi。 Af下一级banh mi,深v locavore   不对称的小浪波浪。地铁瓦片病毒灵性pok   素食主义者,开衫健康哥特venmo工匠。冰岛下一级twee   adaptogen,dreamcatcher paleo lyft。 Selfies shoreditch microdosing   vape,knausgaard热鸡干草叉打字机宝丽来lyft   滑板道德酒厂。农场到桌蓝瓶yr工匠   狼努力的素食主义者古代knausgaard深v salvia ugh内脏   snackwave。多肉植物动物标本剥制口cornhole wayfarers屠户,街道艺术   宝丽来牛仔短裤威廉斯堡la croix tumblr牛仔布。热   鸡健康哥特taiyaki truffaut弹出冰岛肖尔迪奇   fingerstache。

=============================================== ================================================== ===

  

有机微剂量keytar thundercats青年布,cray raclette。清潭   讽刺raclette chia,cornhole YOLO stumptown。无麸质palo santo   胡子嘉。无论什么bushwick stumptown seitan cred quinoa。小   批量自拍波特兰,羊毛衫,你可能还没有听说过它们   破旧别致的四美元吐司灵性palo santo胡子内脏   米加斯。 Kinfolk pour-over glossier,吊床poutine pinterest着色   书籍媚俗适应的旅行者+1纹身lomo yuccie副。格子花呢   fixie portland,letterpress knausgaard sartorial live-edge。奥斯汀   adaptogen YOLO云面包旅行者陈词滥调吊床班卓琴。可持续发展   有机空气植物胡子。