【问题标题】:Counting matrix pairs using a threshold使用阈值计算矩阵对
【发布时间】:2018-06-01 00:32:33
【问题描述】:

我有一个包含数百个 txt 文件的文件夹,我需要分析它们的相似性。下面是我用来运行相似性分析的脚本示例。最后我得到一个数组或矩阵,我可以绘制等。

我想看看有多少对与cos_similarity > 0.5(或我决定使用的任何其他阈值),当然,当我比较相同的文件时删除cos_similarity == 1

其次,我需要基于文件名的这些对的列表。

因此下面示例的输出如下所示:

1

["doc1", "doc4"]

非常感谢您的帮助,因为我不知道该往哪个方向走,有点迷茫。

这是我获取矩阵的脚本示例:

doc1 = "Amazon's promise of next-day deliveries could be investigated amid customer complaints that it is failing to meet that pledge."
doc2 = "The BBC has been inundated with comments from Amazon Prime customers. Most reported problems with deliveries."
doc3 = "An Amazon spokesman told the BBC the ASA had confirmed to it there was no investigation at this time."
doc4 = "Amazon's promise of next-day deliveries could be investigated amid customer complaints..."
documents = [doc1, doc2, doc3, doc4]

# In my real script I iterate through a folder (path) with txt files like this:
#def read_text(path):
#    documents = []
#    for filename in glob.iglob(path+'*.txt'):
#        _file = open(filename, 'r')
#        text = _file.read()
#        documents.append(text)
#    return documents

import nltk, string, numpy
nltk.download('punkt') # first-time use only
stemmer = nltk.stem.porter.PorterStemmer()
def StemTokens(tokens):
    return [stemmer.stem(token) for token in tokens]
remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
def StemNormalize(text):
    return StemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))

nltk.download('wordnet') # first-time use only
lemmer = nltk.stem.WordNetLemmatizer()
def LemTokens(tokens):
    return [lemmer.lemmatize(token) for token in tokens]
remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)
def LemNormalize(text):
    return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))

from sklearn.feature_extraction.text import CountVectorizer
LemVectorizer = CountVectorizer(tokenizer=LemNormalize, stop_words='english')
LemVectorizer.fit_transform(documents)
tf_matrix = LemVectorizer.transform(documents).toarray()

from sklearn.feature_extraction.text import TfidfTransformer
tfidfTran = TfidfTransformer(norm="l2")
tfidfTran.fit(tf_matrix)
tfidf_matrix = tfidfTran.transform(tf_matrix)
cos_similarity_matrix = (tfidf_matrix * tfidf_matrix.T).toarray()

from sklearn.feature_extraction.text import TfidfVectorizer
TfidfVec = TfidfVectorizer(tokenizer=LemNormalize, stop_words='english')
def cos_similarity(textlist):
    tfidf = TfidfVec.fit_transform(textlist)
    return (tfidf * tfidf.T).toarray()
cos_similarity(documents)

输出:

array([[ 1.        ,  0.1459739 ,  0.03613371,  0.76357693],
       [ 0.1459739 ,  1.        ,  0.11459266,  0.19117117],
       [ 0.03613371,  0.11459266,  1.        ,  0.04732164],
       [ 0.76357693,  0.19117117,  0.04732164,  1.        ]])

【问题讨论】:

  • 看!在之前的sn-p中,我可以看到你在左右创建函数。我们只在反复使用同一段代码时才创建函数,因此为了节省时间和精力,我们创建函数。但是,当函数只有一行或者我们将使用它一次左右时,最好不创建它们,这将使代码更清晰、更易读、更容易理解。
  • 你完全正确!我刚刚从在线教程中获取了这个 sn-p,但我会按照你的建议整理它。

标签: python-2.7 nltk tf-idf sklearn-pandas


【解决方案1】:

据我了解您的问题,您想创建一个函数来读取输出 numpy 数组和某个值(阈值)以返回两件事:

  • 有多少文档大于或等于给定阈值
  • 这些文档的名称。

所以,我在这里创建了以下函数,它接受三个参数:

  • cos_similarity() 函数的输出 numpy 数组。
  • 文档名称列表。
  • 某个数字(阈值)。

这里是:

def get_docs(arr, docs_names, threshold):
    output_tuples = []
    for row in range(len(arr)):
        lst = [row+1+idx for idx, num in \
                  enumerate(arr[row, row+1:]) if num >= threshold]
        for item in lst:
            output_tuples.append( (docs_names[row], docs_names[item]) )

    return len(output_tuples), output_tuples

让我们看看它的实际效果:

>>> docs_names = ["doc1", "doc2", "doc3", "doc4"]
>>> arr = cos_similarity(documents)
>>> arr
array([[ 1.        ,  0.1459739 ,  0.03613371,  0.76357693],
   [ 0.1459739 ,  1.        ,  0.11459266,  0.19117117],
   [ 0.03613371,  0.11459266,  1.        ,  0.04732164],
   [ 0.76357693,  0.19117117,  0.04732164,  1.        ]])
>>> threshold = 0.5   
>>> get_docs(arr, docs_names, threshold)
(1, [('doc1', 'doc4')])
>>> get_docs(arr, docs_names, 1)
(0, [])
>>> get_docs(lst, docs_names, 0.13)
(3, [('doc1', 'doc2'), ('doc1', 'doc4'), ('doc2', 'doc4')])

让我们看看这个函数是如何工作的:

  • 首先,我遍历 numpy 数组的每一行。
  • 其次,我遍历行中索引大于行索引的每个项目。所以,我们正在迭代一个像这样的训练形状: 那是因为每对文档在整个数组中被提及两次。我们可以看到arr[0][1]arr[1][0]这两个值是一样的。您还应该注意到,对角线项目没有包括在内,因为我们确定它们是 1,因为每个文档都与它自己非常相似:)。
  • 最后,我们得到值大于或等于给定阈值的项目,并返回它们的索引。这些索引稍后用于获取文档名称。

【讨论】:

  • 正是我想要的。太感谢了。我正在阅读您创建的函数,但不确定我是否理解您如何删除对角线对(num == 1)。请解释一下好吗?
  • 太棒了!非常感谢。
猜你喜欢
  • 2018-09-24
  • 2014-10-14
  • 1970-01-01
  • 1970-01-01
  • 2017-08-21
  • 1970-01-01
  • 2021-04-05
  • 2018-02-07
  • 2020-04-12
相关资源
最近更新 更多