我有一个很大的文档语料库,如果它们有明显的n元语法重叠(在我的例子中,我考虑的是二元语法),我想要合并它们.请考虑以下集合列表:

corpus = [{'example', 'bigram'}, {'another', 'example'}, {'some', 'outlier'}]

我有以下代码来计算相似性矩阵:

import numpy as np

sim_matrix = np.zeros((len(corpus), len(corpus)))
for i in range(len(corpus)):
    for j in range(i+1, len(corpus)):
        sim_matrix[i][j] = get_ngram_overlap(corpus[i], corpus[j])
        
np.fill_diagonal(sim_matrix, 1)

其中,get_ngram_overlap函数的定义如下:

def get_ngram_overlap(ngrams_s1, ngrams_s2):

    if min(len(ngrams_s1), len(ngrams_s2)) == 0:
        return 0

    common_ngrams = ngrams_s1 & ngrams_s2

    return len(common_ngrams)/min(len(ngrams_s1), len(ngrams_s2))

结果是一个MxM矩阵,其中M是我的语料库中n元语法的数量

print(sim_matrix)
>> [[1.  0.5  0.]
   [0.  1.   0.]
   [0.  0.   1.]]

问题是,当M变大时,代码变得非常慢,这变得非常计算昂贵,因为必须比较所有唯一的对.Are there ways to compute it more efficiently?类似于使用优化的距离矩阵计算和适用于字符串集的自定义相似性度量.如有任何帮助,我们不胜感激!

推荐答案

您可以让计算速度更快,首先建立一个索引,然后只对包含常用单词的组合运行get_ngram_overlap():

import numpy as np
from itertools import combinations

# make the items in corpus frozensets (to make them hashable)
corpus = [frozenset({'example', 'bigram'}), frozenset({'another', 'example'}), frozenset({'some', 'outlier'})]

def get_ngram_overlap(ngrams_s1, ngrams_s2):
    mn = min(len(ngrams_s1), len(ngrams_s2))
    if mn == 0:
        return 0
    common_ngrams = ngrams_s1 & ngrams_s2
    return len(common_ngrams)/mn


index, nums = {}, {}
for i, b in enumerate(corpus):
    for word in b:
        index.setdefault(word, []).append(b)
    nums.setdefault(b, []).append(i)

index2 = {}
for k, v in index.items():
    for c in combinations(v, 2):
        index2[c] = get_ngram_overlap(*c)

sim_matrix = np.zeros((len(corpus), len(corpus)))

for a, b in index2:
    for x in nums[a]:
        for y in nums[b]:
            sim_matrix[(x, y) if x < y else (y, x)] = index2[(a, b)]

np.fill_diagonal(sim_matrix, 1)
print(sim_matrix)

打印:

[[1.  0.5 0. ]
 [0.  1.  0. ]
 [0.  0.  1. ]]

一个包含10k个包含500个不同单词的二元语法的基准:

import random
import numpy as np
from timeit import timeit
from itertools import combinations


random.seed(123)
corpus = [frozenset({f'word{random.randint(1, 500)}', f'word{random.randint(1, 500)}'}) for _ in range(10_000)]

def get_ngram_overlap(ngrams_s1, ngrams_s2):
    mn = min(len(ngrams_s1), len(ngrams_s2))
    if mn == 0:
        return 0
    common_ngrams = ngrams_s1 & ngrams_s2
    return len(common_ngrams)/mn


def fn1():
    sim_matrix = np.zeros((len(corpus), len(corpus)))
    for i in range(len(corpus)):
        for j in range(i+1, len(corpus)):
            sim_matrix[i][j] = get_ngram_overlap(corpus[i], corpus[j])

    np.fill_diagonal(sim_matrix, 1)
    return sim_matrix

def fn2():
    index, nums = {}, {}
    for i, b in enumerate(corpus):
        for word in b:
            index.setdefault(word, []).append(b)
        nums.setdefault(b, []).append(i)

    index2 = {}
    for k, v in index.items():
        for c in combinations(v, 2):
            index2[c] = get_ngram_overlap(*c)

    sim_matrix = np.zeros((len(corpus), len(corpus)))

    for a, b in index2:
        for x in nums[a]:
            for y in nums[b]:
                sim_matrix[(x, y) if x < y else (y, x)] = index2[(a, b)]

    np.fill_diagonal(sim_matrix, 1)
    return sim_matrix

assert np.array_equal(fn1(), fn2())

t1 = timeit(fn1, number=1)
t2 = timeit(fn2, number=1)

print(t1)
print(t2)

我的机器上的 fingerprint (Python3.10/AMD Ryzen 5700x):

19.253960204077885
0.37714757514186203

Python相关问答推荐

查找3D数组中沿一个轴的相同值序列的长度(与行程长度编码相关)

将大小为n*512的数组绘制到另一个大小为n*256的数组的PC组件

Python中使用Delivercio进行多个请求

如何匹配3D圆柱体的轴和半径?

使用Beautiful Soup获取第二个srcset属性

使用regex分析具有特定字符的字符串(如果它们存在)

数字梯度的意外值

根据条件将新值添加到下面的行或下面新创建的行中

Python上的Instagram API:缺少client_id参数"

删除所有列值,但判断是否存在任何二元组

在Python Attrs包中,如何在field_Transformer函数中添加字段?

scikit-learn导入无法导入名称METRIC_MAPPING64'

优化pytorch函数以消除for循环

Julia CSV for Python中的等效性Pandas index_col参数

使用groupby Pandas的一些操作

avxspan与pandas period_range

关于Python异步编程的问题和使用await/await def关键字

使用groupby方法移除公共子字符串

基于形状而非距离的两个numpy数组相似性

从列表中获取n个元素,其中list [i][0]== value''