如何高效计算双模网络Jaccard系数?替代低效循环方案
高效计算双模网络的Jaccard系数(百万级专利数据)
针对百万级专利数据+30个类别(435条边)的场景,循环方法效率极低,推荐用稀疏矩阵+矩阵运算的方案,底层优化的矩阵操作比循环快几个数量级,同时大幅节省内存。
步骤1:数据预处理
先对原始数据去重(避免同一专利-类别组合重复计数),然后统计每个类别的专利总数,再构建专利-类别的稀疏二元矩阵(适合百万级数据,内存占用远小于普通矩阵):
library(tidyverse) library(Matrix) # 用于构建稀疏矩阵 # 去重:每个专利-类别组合只保留一次 df_unique <- df %>% distinct(patent, class) # 统计每个class的专利总数(去重后) class_counts <- df_unique %>% count(class, name = "total_patents") # 构建稀疏二元矩阵:行=专利,列=class,值为1表示专利属于该类别 patent_class_mat <- sparseMatrix( i = as.integer(factor(df_unique$patent)), j = as.integer(factor(df_unique$class)), x = 1L, dimnames = list( unique(df_unique$patent), unique(df_unique$class) ) )
步骤2:计算两两类别的交集数量
利用矩阵乘法直接计算所有类别对的交集(同时属于两类的专利数),这一步是底层优化的运算,速度极快:
# 转置后相乘,得到类别对的交集计数矩阵 intersection_mat <- t(patent_class_mat) %*% patent_class_mat # 将矩阵转换为长格式数据框,仅保留上三角(对应combn生成的无序类别对) intersection_df <- as.data.frame(as.matrix(intersection_mat)) %>% rownames_to_column("V1") %>% pivot_longer(cols = -V1, names_to = "V2", values_to = "no_patents_V1_V2") %>% filter(V1 < V2)
步骤3:计算剩余指标与Jaccard系数
合并类别总数,计算仅属于单一类别的专利数,最后套用Jaccard公式:
edge_list_final <- intersection_df %>% # 合并V1的专利总数 left_join(class_counts, by = c("V1" = "class")) %>% rename(no_patents_V1 = total_patents) %>% # 合并V2的专利总数 left_join(class_counts, by = c("V2" = "class")) %>% rename(no_patents_V2 = total_patents) %>% # 计算仅属于V1、仅属于V2的专利数 mutate( no_patents_V1_nV2 = no_patents_V1 - no_patents_V1_V2, no_patents_V2_nV1 = no_patents_V2 - no_patents_V1_V2, # Jaccard系数公式:交集/(并集) = 交集/(V1总数+V2总数-交集) jaccard = no_patents_V1_V2 / (no_patents_V1 + no_patents_V2 - no_patents_V1_V2) )
结果验证(示例数据)
针对你提供的示例数据,最终edge_list_final的核心结果如下:
| V1 | V2 | no_patents_V1_V2 | no_patents_V1 | no_patents_V2 | no_patents_V1_nV2 | no_patents_V2_nV1 | jaccard |
|---|---|---|---|---|---|---|---|
| X | Y | 1 | 2 | 2 | 1 | 1 | 0.333 |
| X | Z | 1 | 2 | 2 | 1 | 1 | 0.333 |
| Y | Z | 2 | 2 | 2 | 0 | 0 | 1.000 |
完全符合手动计算的结果,且处理百万级数据时,整个流程仅需几秒即可完成。
内容的提问来源于stack exchange,提问作者Puneet Sachdeva
相关产品推荐
相关产品推荐

