高效的字符串相似度分组

2023-12-24

Setting: 我有有关人员及其父母姓名的数据,并且我想找到兄弟姐妹(父母姓名相同的人)。

 pdata<-data.frame(parents_name=c("peter pan + marta steward",
                                 "pieter pan + marta steward",
                                 "armin dolgner + jane johanna dough",
                                 "jack jackson + sombody else"))

这里的预期输出将是一列,表明前两个观测值属于 X 族,而第三列和第四列分别属于一个单独的族。例如:

person_id    parents_name                           family_id
1            "peter pan + marta steward",           1
2            "pieter pan + marta steward",          1
3            "armin dolgner + jane johanna dough",  2
4            "jack jackson + sombody else"          3

目前的方法: 我对距离度量很灵活。目前,我使用 Levenshtein 编辑距离来匹配 obs,允许两个字符的差异。但是其他变体,例如“最大公共子字符串”,如果它们运行得更快,那就没问题了。

对于较小的子样本,我使用stringdist::stringdist在循环中或stringdist::stringdistmatrix,但随着样本量的增加,这变得越来越低效。

一旦使用了一定的样本量,矩阵版本就会爆炸。我的循环尝试效率极低:

#create data of the same complexity using random last-names
#(4mio obs and ~1-3 kids per parents) 
pdata<-data.frame(parents_name=paste0(rep(c("peter pan + marta ",
                                "pieter pan + marta ",
                                "armin dolgner + jane johanna ",
                                "jack jackson + sombody "),1e6),stringi::stri_rand_strings(4e6, 5)))

for (i in 1:nrow(pdata)) {
  similar_fatersname0<-stringdist::stringdist(pdata$parents_name[i],pdata$parents_name[i:nrow(pdata)],nthread=4)<2
  #[create grouping indicator]
}

我的问题:应该会显着提高效率,例如因为一旦我发现它们在更容易评估的东西上有足够的不同,我就可以停止比较字符串,例如。字符串长度,或第一个单词。字符串长度变体已经可以工作并将复杂性降低约 3 倍。但这还太少了。任何减少计算时间的建议都值得赞赏。

Remarks:

  • 这些字符串实际上采用 unicode,而不是拉丁字母 (Devnagari)
  • 完成预处理以删除未使用的字符等

有两个挑战:

A. Levenshtein distance的并行执行——而不是顺序循环

B. 比较次数:如果我们的源列表有 400 万个条目,理论上我们应该运行 16 万亿次 Levenstein 距离度量,这是不现实的,即使我们解决了第一个挑战。

为了使我对语言的使用更加清晰,以下是我们的定义

  • 我们想要测量表达式之间的编辑距离。
  • 每个表达式都有两个部分,父级 A 全名和父级 B 全名,用加号分隔
  • 各部分的顺序很重要(即,如果表达式 1 的父级 A = 表达式 2 的父级 A 且父级 B 或表达式 1= 表达式 2 的父级 B,则两个表达式 (1, 2) 是相同的。如果父级表达式 1 的 A = 表达式 2 的父级 B 且表达式 1 的父级 B = 表达式 2 的父级 A)
  • 部分(或全名)是一系列单词,由空格或破折号分隔,对应于一个人的名字和姓氏
  • 我们假设一个部分中的最大单词数为 6(您的示例有 2 或 3 个单词的部分,我假设我们最多可以有 6 个) 部分中的单词顺序很重要(该部分始终是名字后面是姓氏,而不是姓氏在前,例如 Jack John 和 John Jack 是两个不同的人)。
  • 有400万个表达方式
  • 假定表达式仅包含英文字符。数字、空格、标点符号、破折号和任何非英文字符都可以忽略
  • 我们假设简单匹配已经完成(如精确表达式匹配),并且我们不必搜索精确匹配

从技术上讲,目标是在 400 万个表达式列表中找到一系列匹配的表达式。如果两个表达式的 Levenstein 距离小于 2,则它们被视为匹配表达式。

实际上,我们创建了两个列表,它们是最初 400 万个表达式列表的精确副本。我们称之为左列表和右列表。在复制列表之前,每个表达式都会被分配一个表达式 id。 我们的目标是找到 Right 列表中与 Left 列表的条目的 Levenstein 距离小于 2 的条目,不包括相同的条目(相同的表达式 id)。

我建议采用两步法来分别解决这两个挑战。第一步将减少可能匹配表达式的列表,第二步将简化 Levenstein 距离测量,因为我们只查看非常接近的表达式。使用的技术是任何传统的数据库服务器,因为我们需要对数据集进行索引以提高性能。

挑战A

挑战 A 包括减少距离测量的数量。我们从最大值大约开始。 16万亿(400万的2次方),我们不应该超过几千万或几亿。 这里使用的技术包括在完整的表达式中搜索至少一个相似的单词。根据数据的分布方式,这将大大减少可能的匹配对的数量。或者,根据结果所需的准确性,我们还可以搜索具有至少两个相似单词或至少一半相似单词的对。

从技术上讲,我建议将表达式列表放在表格中。添加标识列以为每个表达式创建唯一的 ID,并创建 12 个字符列。然后解析表达式并将每个部分的每个单词放在单独的列中。这看起来像(我没有表示所有 12 列,但想法如下):

|id | expression | sect_a_w_1 | sect_a_w_2 | sect_b_w_1 |sect_b_w_2 |
|1 | peter pan + marta steward | peter | pan | marta |steward      |

虽然有空列(因为 12 个单词的表达很少),但这并不重要。

然后我们复制该表并在每个部分...列上创建一个索引。 我们运行 12 个连接来尝试查找相似的单词,例如

SELECT L.id, R.id 
FROM left table L JOIN right table T 
ON L.sect_a_w_1 = R.sect_a_w_1
AND L.id <> R.id 

我们收集 12 个临时表中的输出,并对这 12 个表运行联合查询,以获取所有表达式的简短列表,这些表达式具有至少一个相同单词的潜在匹配表达式。这是我们的挑战 A 的解决方案。我们现在有一个最可能匹配对的简短列表。该列表将包含数百万条记录(左右条目对),但不是数十亿条。

挑战B

挑战 B 的目标是批量处理简化的 Levenstein 距离(而不是循环运行)。 首先,我们应该就什么是简化的莱文斯坦距离达成一致。 首先我们同意两个表达式的levenstein距离是两个表达式中具有相同索引的所有单词的levenstein距离之和。我的意思是两个表达式的 Levenstein 距离是它们的两个第一个单词的距离,加上它们的两个第二个单词的距离,等等。 其次,我们需要发明一个简化的莱文斯坦距离。我建议使用 n-gram 方法,仅包含 2 个字符的克,其索引绝对差小于 2 。

例如peter和pieter之间的距离计算如下

Peter       
1 = pe          
2 = et          
3 = te          
4 = er
5 = r_           

Pieter
1 = pi
2 = ie
3 = et
4 = te
5 = er
6 = r_ 

Peter 和 Pieter 有 4 个常见的 2-gram,索引绝对差小于 2 'et'、'te'、'er'、'r_'。两个单词中最大的一个有 6 个可能的 2-gram,则距离为 6-4 = 2 - Levenstein 距离也将为 2,因为有 1 个“eter”移动和一个字母插入“i”。

这是一个近似值,并不适用于所有情况,但我认为在我们的情况下它会工作得很好。如果我们对结果的质量不满意,我们可以尝试使用 3 克或 4 克或允许大于 2 克的序列差异。但其想法是每对执行的计算量比传统的 Levenstein 算法要少得多。

然后我们需要将其转化为技术解决方案。我之前所做的如下: 首先隔离单词:由于我们只需要测量单词之间的距离,然后将每个表达式的这些距离相加,我们可以通过在单词列表上运行不同的选择来进一步减少计算次数(我们已经准备好了单词列表)上一节中的单词)。

这种方法需要一个映射表来跟踪表达式id、部分id、单词id和单词的单词序列号,以便可以在过程结束时计算原始表达式距离。

然后我们得到一个更短的新列表,并且包含与 2 克距离度量相关的所有单词的交叉连接。 然后我们想要批量处理这个 2-gram 距离测量,我建议在 SQL 连接中进行。这需要一个预处理步骤,其中包括创建一个新的临时表,将每个 2-gram 存储在单独的行中,并跟踪单词 Id、单词序列和部分类型

从技术上讲,这是通过使用一系列(或循环)子字符串选择对单词列表进行切片来完成的,如下所示(假设单词列表表 - 有两个副本,一个左副本和一个右副本 - 包含 2 列 word_id 和 word):

INSERT INTO left_gram_table (word_id, gram_seq, gram)
SELECT word_id, 1 AS gram_seq, SUBSTRING(word,1,2) AS gram
FROM left_word_table 

进而

INSERT INTO left_gram_table (word_id, gram_seq, gram)
SELECT word_id, 2 AS gram_seq, SUBSTRING(word,2,2) AS gram
FROM left_word_table 

Etc.

使“steward”看起来像这样的东西(假设单词 id 是 152)

|  pk  | word_id | gram_seq | gram | 
|  1   |  152       |  1          | st |
|  2   |  152       |  2          | te |
|  3   |  152       |  3          |  ew |
|  4   |  152       |  4          |  wa |
|  5   |  152       |  5          |  ar |
|  6   |  152       |  6          |  rd |
|  7   |  152       |  7          |  d_ |

不要忘记在 word_id、gram 和 gram_seq 列上创建索引,距离可以通过左右 gram 列表的连接来计算,其中 ON 看起来像

ON L.gram = R.gram 
AND ABS(L.gram_seq + R.gram_seq)< 2 
AND L.word_id <> R.word_id 

距离是两个单词中最长的单词的长度减去匹配克的数量。 SQL 进行此类查询的速度非常快,我认为一台具有 8 GB RAM 的简单计算机可以在合理的时间范围内轻松执行数亿行。

然后只需连接映射表即可计算每个表达式中单词到单词的距离之和,从而得到总的表达式到表达式的距离。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

高效的字符串相似度分组 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 在 nHibernate 关系中使用实体的 Lite 版本?

    在某些情况下 出于性能原因 创建一个实体的轻量级版本 指向同一个表 但映射的列较少 这是一个好主意吗 例如 如果我有一个包含 50 列的联系人表 并且在一些相关实体中 我可能对 FirstName 和 LastName 属性感兴趣 那么创建
  • Python:字符串不会转换为浮点数[重复]

    这个问题在这里已经有答案了 我几个小时前写了这个程序 while True print What would you like me to double line raw input gt if line done break else f
  • 如何计算特定字符在字符串中出现的次数

    我正在尝试创建一个函数来查看数组中的任何字符是否在字符串中 如果是 有多少个 我尝试计算每一种模式 但是太多了 我尝试使用 Python 中的 in 运算符的替代方案 但效果不佳 function calc fit element var
  • ddply 和aggregate 之间的区别

    有人可以通过以下示例帮助我了解聚合和 ddply 之间的区别 数据框 mydat lt data frame first rpois 10 10 second rpois 10 10 third rpois 10 10 group c re
  • 如何从 R keras 中的类似生成器的数据中评估()和预测()

    我有以下代码 数据集可以下载here https www dropbox com s qjt5o31oyqj10m8 data tar gz dl 0 or here https www kaggle com c dogs vs cats
  • 计算 R 中各列的唯一值

    我正在尝试创建一个新变量 其中包含来自两个不同列的字符串值的唯一计数 所以我有这样的东西 例如 A tibble 4 x 2 names partners
  • 找到对应的未经V8优化的JS代码源

    我尝试优化 node js 应用程序的性能 因此我正在分析 V8 的 JIT 编译器的行为 当通过运行应用程序时node trace deopt trace opt code comments print optcode 输出包含许多重复出
  • Python:删除字符串开头的数字

    我有一些这样的字符串 string1 123 123 This is a string some other numbers string2 1 This is a string some numbers string3 12 3 12 T
  • 纵向比较 R 中的值...并进行扭转

    我有许多人在多达四个时间段进行的测试结果 这是一个示例 dat lt structure list Participant ID c A A A A B B B B C C C C phase structure c 1L 2L 3L 4L
  • 如何在 python 3.x 中使用 string.replace()

    The string replace 在 python 3 x 上已弃用 这样做的新方法是什么 与 2 x 一样 使用str replace https docs python org library stdtypes html str r
  • 使用 enum.values() 与字符串数组相比,性能是否会受到影响?

    我正在使用枚举来替换String我的 java 应用程序 JRE 1 5 中的常量 当我在不断调用的方法中将枚举视为名称的静态数组时 例如 在渲染 UI 时 是否会对性能造成影响 我的代码看起来有点像这样 public String get
  • R:如何获取该月的周数

    我是 R 新手 我想要该日期所属月份的周数 通过使用以下代码 gt CurrentDate lt Sys Date gt Week Number lt format CurrentDate format U gt Week Number 3
  • 投资决策:R中的NPV、IRR、PB计算

    我正在尝试计算不同数量项目的净现值 NPV 内部收益率 IRR 和投资回收期 PB 时间 以评估哪个投资项目提供最佳回报 到目前为止 我可以为每个项目单独计算几行代码 但我想做的是 编写一个函数 它接受一个包含许多不同项目及其现金流的矩阵
  • 如何加速Python中的N维区间树?

    考虑以下问题 给定一组n间隔和一组m浮点数 对于每个浮点数 确定包含该浮点数的区间子集 这个问题已经通过构建一个解决区间树 https en wikipedia org wiki Interval tree 或称为范围树或线段树 已经针对一
  • 字符串数组文本格式化

    我有这个字符串 String text Address 1 Street nr 45 Address 2 Street nr 67 Address 3 Street nr 56 n Phone number 000000000 稍后将被使用
  • 如何修复 R 中 Kaplan Meier 图的风险表计算错误

    以下是一个数据帧 其中 6 个参与者中的每一个都有唯一的 record ID 我想绘制一个生存分析图 其中包含感兴趣事件的复发以及在时间间隔 tstart 到 tstop 内 暴露 药物剂量 数值变量 的时间依赖性协变量 每个参与者的最大
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 麦当劳 omega:R 中的警告

    我正在计算几种不同尺度的欧米茄 并在 R 中使用不同的 omega 函数获取不同比例的不同警告消息 我的问题是如何解释这些警告以及报告检索到的 omega 统计数据是否安全 当我使用 从 alpha 到 omega 内部一致性估计普遍问题的

随机推荐