【问题标题】:Approaches for finding matches in a large dataset在大型数据集中查找匹配的方法
【发布时间】:2020-11-16 05:29:29
【问题描述】:

我有一个项目,给定约 10,000 个唯一字符串的列表,我想找到这些字符串在包含 10,000,000 多个字符串条目的文件中出现的位置。如果可能的话,我还想包括部分匹配。我的约 10,000 个字符串列表是动态数据,每 30 分钟更新一次,目前我无法处理所有搜索以跟上更新的数据。我的搜索现在大约需要 3 个小时(相比之下,我必须在 30 分钟内进行搜索),所以我觉得我解决这个问题的方法不太正确。

我目前的方法是首先从 10,000,000+ 个字符串条目中创建一个列表。然后使用搜索在更大的列表中搜索动态列表中的每个项目。

results_boolean = [keyword in n for n in string_data]

有没有一种方法可以通过更合适的方法大大加快速度?

【问题讨论】:

  • 您需要将其中一个列表做成字典或集合,我建议将 10,000 个项目作为一个集合,减少存储空间。也许用 C++ 更快地完成它会更好
  • 是“唯一字符串”单字吗?
  • 你的完整代码是什么?
  • string_data 是什么样的?它是二维列表吗?它从何而来? (IE你是怎么得到的?)

标签: python search large-data


【解决方案1】:

一般来说,您希望预处理大量不变的数据以加快重复搜索的速度。但是你说得太少了,无法提出明显实用的建议。比如:这些字符串有多长?什么是字母表(例如,7 位 ASCII 或完整的 Unicode?)?总共有多少个字符?字母表中的字符是否同样可能出现在每个字符串位置,或者分布是否高度倾斜?如果是这样,怎么做?以此类推。

这是关于最简单的索引类型,构建一个条目数等于所有string_data 中唯一字符数的字典。它将每个字符映射到包含该字符的字符串的string_data 索引集。然后,可以将关键字的搜索限制为现在预先知道包含关键字第一个字符的唯一 string_data 条目。

现在,根据您所说的无法猜到的细节,可能即使是这种适度的索引也会消耗比您拥有的更多的 RAM - 或者它是可能它已经足以让您获得所需的 6 倍加速:

# Preprocessing - do this just once, when string_data changes.
def build_map(string_data):
    from collections import defaultdict
    ch2ixs = defaultdict(set)
    for i, s in enumerate(string_data):
        for ch in s:
            ch2ixs[ch].add(i)
    return ch2ixs

def find_partial_matches(keywords, string_data, ch2ixs):
    for keyword in keywords:
        ch = keyword[0]
        if ch in ch2ixs:
            result = []
            for i in ch2ixs[ch]:
                if keyword in string_data[i]:
                    result.append(i)
            if result:
                print(repr(keyword), "found in strings", result)

然后,例如,

string_data = ['banana', 'bandana', 'bandito']
ch2ixs = build_map(string_data)

find_partial_matches(['ban', 'i', 'dana', 'xyz', 'na'],
                     string_data,
                     ch2ixs)

显示:

'ban' found in strings [0, 1, 2]
'i' found in strings [2]
'dana' found in strings [1]
'na' found in strings [0, 1]

例如,如果您仍然有足够的 RAM,但需要更快的速度,并且愿意放弃(可能很愚蠢 - 但不能从这里猜测)1 字符匹配,您可以改为索引二元组(相邻字母对)。

在限制中,您可以使用string_data 构建一个尝试,这将需要大量 RAM,但可以将搜索嵌入关键字的时间减少到与关键字中的字符数成正比的操作数,与string_data 中有多少个字符串无关。

请注意,您确实应该找到摆脱这种情况的方法:

results_boolean = [keyword in n for n in string_data]

为每个关键字搜索构建一个包含超过 1000 万个条目的列表会使每次搜索都变得昂贵,无论您多么巧妙地索引数据。

注意:可能对上述内容的实际改进是将搜索限制为包含所有关键字字符的字符串:

def find_partial_matches(keywords, string_data, ch2ixs):
    for keyword in keywords:
        keyset = set(keyword)
        if all(ch in ch2ixs for ch in keyset):
            ixs = set.intersection(*(ch2ixs[ch] for ch in keyset))
            result = []
            for i in ixs:
                if keyword in string_data[i]:
                    result.append(i)
            if result:
                print(repr(keyword), "found in strings", result)

【讨论】:

    【解决方案2】:

    使用带有集合的生成器可能是你最好的选择......我认为这个解决方案会起作用并且可能更快

    def find_matches(target_words,filename_to_search):
        targets = set(target_words)
        with open("search_me.txt") as f:
            for line_no,line in enumerate(f):
                matching_intersection = targets.intersection(line.split())
                if matching_intersection:
                    yield (line_no,line,matching_intersection) # there was a match
        
    for match in find_matches(["unique","list","of","strings"],"search_me.txt"):
        print("Match: %s"%(match,))
        input("Hit Enter For next match:") #py3 ... just to see your matches
    

    如果你的匹配不是单个单词,它会变得更难,特别是如果没有可靠的分组分隔符

    【讨论】:

    • 我想指出这个解决方案如何逐行读取文件并返回生成器的重要性。这允许从任意大的文件中读取字符串数据而不会耗尽 RAM。
    • 粗略的在速度上有一个权衡(对于这种类型的操作来说不是很多)......但如果它不够快,我认为 OP 将需要下降到 c/c++ .. . 即使那样我也不确定他们会得到多少加速
    • 对于这种规模的集合来说,使用集合而不是列表进行成员测试已经快了几个数量级;我想他会没事的:)
    猜你喜欢
    • 2011-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-26
    • 2013-04-17
    • 1970-01-01
    • 2018-07-20
    • 2023-03-19
    相关资源
    最近更新 更多