- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我目前正在处理一个非常大的位置数据库,并试图将它们与它们的真实世界坐标相匹配。
为此,我下载了 the geoname dataset其中包含很多条目。它给出了可能的名称和纬度/经度坐标。为了尝试加快这个过程,我通过删除对我的数据集没有意义的条目,设法将巨大的 csv 文件(1.6 GB)减少到 0.450 GB。但是它仍然包含 400 万个条目。
现在我有很多条目,例如:
知道字符串与这么长的字符串匹配,我使用了 Standford's NER通过 NLTK 获得更好的字符串来限定我的位置。现在我有这样的字符串:
geoname 数据集包含如下内容:
我正在应用这个 algorithm在我的条目和包含 4M 条目的 geoname csv 之间获得良好的可能匹配。我首先读取 geoname_cleaned.csv 文件并将所有数据放入列表中。对于每个条目,我然后在当前条目和 geoname_list 的所有条目之间调用我的每个条目 string_similarity()
def get_bigrams(string):
"""
Take a string and return a list of bigrams.
"""
s = string.lower()
return [s[i:i+2] for i in list(range(len(s) - 1))]
def string_similarity(str1, str2):
"""
Perform bigram comparison between two strings
and return a percentage match in decimal form.
"""
pairs1 = get_bigrams(str1)
pairs2 = get_bigrams(str2)
union = len(pairs1) + len(pairs2)
hit_count = 0
for x in pairs1:
for y in pairs2:
if x == y:
hit_count += 1
break
return (2.0 * hit_count) / union
我已经在我的原始数据集的一个子集上测试了该算法,它工作正常,但它显然非常慢(单个位置最多需要 40 秒)。由于我有超过一百万的条目要处理,这将花费 10000 小时或更长时间。我想知道你们是否知道如何加快速度。我显然想到了并行处理,但我没有任何可用的 HPC 解决方案。也许简单的想法可以帮助我加快速度。
我对你们可能有的任何想法持开放态度,但不知何故更喜欢与 python 兼容的解决方案。
提前致谢:)。
编辑:
我已经用 fuzz.token_set_ratio(s1, s2)
尝试了 fuzzywuzzy,但它的性能最差(运行时间更差,结果也不太好)。使用我的自定义技术时,比赛不如以前那么好,单次比赛的比赛时间增加了 15 秒。
编辑 2:
虽然我也曾在开始时使用某种排序来帮助匹配,但我的天真实现行不通。但我确信有一些方法可以加快速度,比如删除 geoname 数据集中的一些条目,或者以某种方式对它们进行排序。我已经做了很多清理以删除无用的条目,但无法获得低于 4M 的数字
最佳答案
我们可以通过多种方式加快匹配速度。我假设在您的代码中 str1
是您数据集中的名称,而 str2
是一个地理名称字符串。为了测试代码,我根据您问题中的数据制作了两个小数据集。我编写了两个匹配函数 best_match
和 first_match
,它们使用您当前的 string_similarity
函数,因此我们可以看到我的策略给出了相同的结果。 best_match
检查所有 geoname 字符串,如果超过给定的阈值分数,则返回得分最高的字符串,否则返回 None
。 first_match
(可能)更快:它只返回第一个超过阈值的 geoname 字符串,如果找不到,则返回 None
,所以如果找不到一个匹配然后它仍然必须搜索整个 geoname 列表。
在我的改进版本中,我们为每个 str1
生成一次二元组,而不是为每个 str2
重新生成 str1
的二元组我们将其与之进行比较。我们预先计算所有的 geoname 双字母组,将它们存储在由字符串索引的字典中,这样我们就不必为每个 str
重新生成它们。此外,我们将 geoname 双字母组存储为集合。这使得 hit_count
的计算速度更快,因为集合成员测试比对字符串列表进行线性扫描要快得多。 geodict
还需要存储每个二元组的长度:一个集合不包含重复项,因此二元组集合的长度可能小于二元组列表,但我们需要列表长度来正确计算分数。
# Some fake data
geonames = [
'Slettmarkmountains Jotunheimen Norway',
'Fairy Glen Skye Scotland UK',
'Emigrant Wilderness California',
'Yosemite National Park',
'Half Dome Yosemite National Park',
]
mynames = [
'Jotunheimen Norway',
'Fairy Glen',
'Slettmarkmountains Jotunheimen Norway',
'Bryce Canyon',
'Half Dome',
]
def get_bigrams(string):
"""
Take a string and return a list of bigrams.
"""
s = string.lower()
return [s[i:i+2] for i in range(len(s) - 1)]
def string_similarity(str1, str2):
"""
Perform bigram comparison between two strings
and return a percentage match in decimal form.
"""
pairs1 = get_bigrams(str1)
pairs2 = get_bigrams(str2)
union = len(pairs1) + len(pairs2)
hit_count = 0
for x in pairs1:
for y in pairs2:
if x == y:
hit_count += 1
break
return (2.0 * hit_count) / union
# Find the string in geonames which is the best match to str1
def best_match(str1, thresh=0.2):
score, str2 = max((string_similarity(str1, str2), str2) for str2 in geonames)
if score < thresh:
str2 = None
return score, str2
# Find the 1st string in geonames that matches str1 with a score >= thresh
def first_match(str1, thresh=0.2):
for str2 in geonames:
score = string_similarity(str1, str2)
if score >= thresh:
return score, str2
return None
print('Best')
for mystr in mynames:
print(mystr, ':', best_match(mystr))
print()
print('First')
for mystr in mynames:
print(mystr, ':', best_match(mystr))
print()
# Put all the geoname bigrams into a dict
geodict = {}
for s in geonames:
bigrams = get_bigrams(s)
geodict[s] = (set(bigrams), len(bigrams))
def new_best_match(str1, thresh=0.2):
pairs1 = get_bigrams(str1)
pairs1_len = len(pairs1)
score, str2 = max((2.0 * sum(x in pairs2 for x in pairs1) / (pairs1_len + pairs2_len), str2)
for str2, (pairs2, pairs2_len) in geodict.items())
if score < thresh:
str2 = None
return score, str2
def new_first_match(str1, thresh=0.2):
pairs1 = get_bigrams(str1)
pairs1_len = len(pairs1)
for str2, (pairs2, pairs2_len) in geodict.items():
score = 2.0 * sum(x in pairs2 for x in pairs1) / (pairs1_len + pairs2_len)
if score >= thresh:
return score, str2
return None
print('New Best')
for mystr in mynames:
print(mystr, ':', new_best_match(mystr))
print()
print('New First')
for mystr in mynames:
print(mystr, ':', new_first_match(mystr))
print()
输出
Best
Jotunheimen Norway : (0.6415094339622641, 'Slettmarkmountains Jotunheimen Norway')
Fairy Glen : (0.5142857142857142, 'Fairy Glen Skye Scotland UK')
Slettmarkmountains Jotunheimen Norway : (1.0, 'Slettmarkmountains Jotunheimen Norway')
Bryce Canyon : (0.1875, None)
Half Dome : (0.41025641025641024, 'Half Dome Yosemite National Park')
First
Jotunheimen Norway : (0.6415094339622641, 'Slettmarkmountains Jotunheimen Norway')
Fairy Glen : (0.5142857142857142, 'Fairy Glen Skye Scotland UK')
Slettmarkmountains Jotunheimen Norway : (1.0, 'Slettmarkmountains Jotunheimen Norway')
Bryce Canyon : (0.1875, None)
Half Dome : (0.41025641025641024, 'Half Dome Yosemite National Park')
New Best
Jotunheimen Norway : (0.6415094339622641, 'Slettmarkmountains Jotunheimen Norway')
Fairy Glen : (0.5142857142857142, 'Fairy Glen Skye Scotland UK')
Slettmarkmountains Jotunheimen Norway : (1.0, 'Slettmarkmountains Jotunheimen Norway')
Bryce Canyon : (0.1875, None)
Half Dome : (0.41025641025641024, 'Half Dome Yosemite National Park')
New First
Jotunheimen Norway : (0.6415094339622641, 'Slettmarkmountains Jotunheimen Norway')
Fairy Glen : (0.5142857142857142, 'Fairy Glen Skye Scotland UK')
Slettmarkmountains Jotunheimen Norway : (1.0, 'Slettmarkmountains Jotunheimen Norway')
Bryce Canyon : None
Half Dome : (0.41025641025641024, 'Half Dome Yosemite National Park')
new_first_match
相当简单。线路
for str2, (pairs2, pairs2_len) in geodict.items():
遍历 geodict
中的每个项目,提取每个字符串、二元组和真正的二元组长度。
sum(x in pairs2 for x in pairs1)
计算 pairs1
中有多少二元组是 pairs2
集合的成员。
因此,对于每个 geoname 字符串,我们计算相似性分数并在它 >= 阈值(默认值为 0.2)时返回它。您可以给它一个不同的默认 thresh
,或者在调用它时传递一个 thresh
。
new_best_match
有点复杂。 ;)
((2.0 * sum(x in pairs2 for x in pairs1) / (pairs1_len + pairs2_len), str2)
for str2, (pairs2, pairs2_len) in geodict.items())
是一个生成器表达式。它遍历 geodict
项并为每个 geoname 字符串创建一个 (score, str2)
元组。然后,我们将该生成器表达式提供给 max
函数,该函数返回得分最高的元组。
这是 new_first_match
的一个版本,它实现了 juvian 在评论中提出的建议。它可能会节省一点时间。此版本还避免测试任一二元语法是否为空。
def new_first_match(str1, thresh=0.2):
pairs1 = get_bigrams(str1)
pairs1_len = len(pairs1)
if not pairs1_len:
return None
hiscore = 0
for str2, (pairs2, pairs2_len) in geodict.items():
if not pairs2_len:
continue
total_len = pairs1_len + pairs2_len
bound = 2.0 * pairs1_len / total_len
if bound >= hiscore:
score = 2.0 * sum(x in pairs2 for x in pairs1) / total_len
if score >= thresh:
return score, str2
hiscore = max(hiscore, score)
return None
一个更简单的变体是不计算 hiscore
& 只是比较 bound
和 thresh
。
关于python - 加速 "closest"字符串匹配算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52040353/
使用sed和/或awk,仅在行包含字符串“ foo”并且行之前和之后的行分别包含字符串“ bar”和“ baz”时,我才希望删除行。 因此,对于此输入: blah blah foo blah bar
例如: S1: "some filename contains few words.txt" S2:“一些文件名包含几个单词 - draft.txt” S3:“一些文件名包含几个单词 - 另一个 dr
我正在尝试处理一些非常困惑的数据。我需要通过样本 ID 合并两个包含不同类型数据的大数据框。问题是一张表的样本 ID 有许多不同的格式,但大多数都包含用于匹配其 ID 中某处所需的 ID 字符串,例如
我想在匹配特定屏幕尺寸时显示特定图像。在这种情况下,对于 Bootstrap ,我使用 col-xx-## 作为我的选择。但似乎它并没有真正按照我认为应该的方式工作。 基本思路,我想显示一种全屏图像,
出于某种原因,这条规则 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*
我想做类似的东西(Nemerle 语法) def something = match(STT) | 1 with st= "Summ" | 2 with st= "AVG" =>
假设这是我的代码 var str="abc=1234587;abc=19855284;abc=1234587;abc=19855284;abc=1234587;abc=19855284;abc=123
我怎样才能得到这个字符串的数字:'(31.5393701, -82.46235569999999)' 我已经在尝试了,但这离解决方案还很远:) text.match(/\((\d+),(\d+)\)/
如何去除输出中的逗号 (,)?有没有更好的方法从字符串或句子中搜索 url。 alert(" http://www.cnn.com df".match(/https?:\/\/([-\w\.]+
a = ('one', 'two') b = ('ten', 'ten') z = [('four', 'five', 'six'), ('one', 'two', 'twenty')] 我正在尝试
我已经编写了以下代码,我希望用它来查找从第 21 列到另一张表中最后一行的值,并根据这张表中 A 列和另一张表中 B 列中的值将它们返回到这张表床单。 当我使用下面的代码时,我得到一个工作表错误。你能
我在以下结构中有两列 A B 1 49 4922039670 我已经能够评估 =LEN(A1)如2 , =LEFT(B1,2)如49 , 和 =LEFT(B1,LEN(A1)
我有一个文件,其中一行可以以 + 开头, -或 * .在其中一些行之间可以有以字母或数字(一般文本)开头的行(也包含这些字符,但不在第 1 列中!)。 知道这一点,设置匹配和突出显示机制的最简单方法是
我有一个数据字段文件,其中可能包含注释,如下所示: id, data, data, data 101 a, b, c 102 d, e, f 103 g, h, i // has to do with
我有以下模式:/^\/(?P.+)$/匹配:/url . 我的问题是它也匹配 /url/page ,如何忽略/在这个正则表达式中? 该模式应该: 模式匹配:/url 模式不匹配:/url/page 提
我有一个非常庞大且复杂的数据集,其中包含许多对公司的观察。公司的一些观察是多余的,我需要制作一个键来将多余的观察映射到一个单独的观察。然而,判断他们是否真的代表同一家公司的唯一方法是通过各种变量的相似
我有以下 XML A B C 我想查找 if not(exists(//Record/subRecord
我制作了一个正则表达式来验证潜在的比特币地址,现在当我单击报价按钮时,我希望根据正则表达式检查表单中输入的值,但它不起作用。 https://jsfiddle.net/arkqdc8a/5/ var
我有一些 MS Word 文档,我已将其全部内容转移到 SQL 表中。 内容包含多个方括号和大括号,例如 [{a} as at [b],] {c,} {d,} etc 我需要进行检查以确保括号平衡/匹
我正在使用 Node.js 从 XML 文件读取数据。但是当我尝试将文件中的数据与文字进行比较时,它不匹配,即使它看起来相同: const parser: xml2js.Parser = new
我是一名优秀的程序员,十分优秀!