一组单词中的模式并将它们分组

Pattern in a set of words and group them

我需要在一组 5000 个样本中找出多个词之间的相关性。

样本:-

  1. 芒果、番石榴、荔枝、苹果
  2. 芒果、番石榴、荔枝、橙子
  3. 芒果、番石榴、菠萝、葡萄
  4. 钢笔、铅笔、书本、复印件、笔记本
  5. 钢笔、铅笔、书本、复印件、秤

我们看到 1 和 2 彼此非常接近。 3 几乎接近 1 和 2。我们还有 4 和 5 彼此非常接近。

我们可以使用什么方法和技术来检查这种相关性?

提前致谢!

修订:在分组方面也需要帮助,例如 A 组包含第 1、2、3 行,B 组包含第 4 和 5 行。?

这是解决此问题的一种方法。我使用 scikit-learn 将每个列表转换为文档术语矩阵。然后使用 scipy.spacial.distance.

计算每行之间的余弦相似度矩阵
from sklearn.feature_extraction.text import CountVectorizer
from scipy.spatial import distance

count_vect = CountVectorizer(tokenizer=lambda x: x.split(', '))

ls = ['mango, guava, litchi, apple', 
      'mango, guava, litchi, orange',
      'mango, guava, pineapple, grape',
      'pen, pencil, book, copy, notebook',
      'pen, pencil, book, copy, scale']

X = count_vect.fit_transform(ls).toarray()
D = distance.cdist(X, X, metric='cosine')

输出是每行之间的距离矩阵。看起来像下面这样:

[[ 0.  ,  0.25,  0.5 ,  1.  ,  1.  ],
 [ 0.25,  0.  ,  0.5 ,  1.  ,  1.  ],
 [ 0.5 ,  0.5 ,  0.  ,  1.  ,  1.  ],
 [ 1.  ,  1.  ,  1.  ,  0.  ,  0.2 ],
 [ 1.  ,  1.  ,  1.  ,  0.2 ,  0.  ]])

例如D[0, 1]表示第1行靠近第2行,因为两行之间的距离很小。此外,您可以看到 D[3, 4] 很小,这意味着第 4 行接近第 5 行。

注意 你也可以考虑使用 distance.pdist(X, metric='cosine') 给出矩阵的下对角线只是因为下对角线和上对角线是相等的。

分组文件

为了更花哨,您可以使用层次聚类将每一行与计算的距离矩阵聚类在一起。

from scipy.cluster import hierarchy

D = distance.pdist(X, metric='cosine')
Z = hierarchy.linkage(D, metric='euclidean')
partition = hcluster.fcluster(Z, t=0.8, criterion='distance') # [2, 2, 2, 1, 1] 

这意味着文档 1、2、3 被分到第 2 组,4、5 被分到第 1 组。如果绘制树状图,您可以看到每一行如何聚集在一起

from scipy.cluster.hierarchy import dendrogram
import matplotlib.pyplot as plt

hierarchy.dendrogram(Z, above_threshold_color='#bcbddc',
                     orientation='top')

另一种方法,或者可能是解决您问题的新开始的另一种想法:

import re
from itertools import chain

a = ['mango, guava, litchi, apple', 
      'mango, guava, litchi, orange',
      'mango, guava, pineapple, grape',
      'pen, pencil, book, copy, notebook',
      'pen, pencil, book, copy, scale']

def get_words(lst):
    return [re.findall(r'[\w]+', k) for k in a]

def get_percent(lst):
    groupped_valid_dict = {}
    for k in range(len(lst)):
        sub = []
        for j in range(k+1, len(lst)):
            s = sum([1 if m == n else 0 for m, n in zip(lst[k], lst[j])])
            #percent = (1 - float(len(lst[k]) - s)/len(lst[k])) * 100
            #fmt = '%.2f%%' % percent
            #print 'Words of lines: %d and %d are %s close' %(k+1, j+1, fmt)
            if s > 0:
                sub.append("Line{}".format(j+1))
        if sub:
            groupped_valid_dict["Line{}".format(k+1)] = sub
    return groupped_valid_dict


lst = get_words(a)
lines  = get_percent(lst)
groups = [[k] + lines[k] for k in lines if k not in chain.from_iterable(lines.values())]
groups.sort(key=lambda x: x[0])

for k, v in enumerate(groups, 1):
    print "Group%d" %k, v

输出:

Group1 ['Line1', 'Line2', 'Line3']
Group2 ['Line4', 'Line5']