【问题标题】:Python: What is an efficient way to loop over a list of strings and group substrings in the list?Python:在列表中循环字符串列表和分组子字符串的有效方法是什么?
【发布时间】:2022-09-27 13:12:49
【问题描述】:

背景

mylist = [\'abc123\', \'abc123456\', \'abc12355\', \'def456\', \'ghi789\', \'def4567\', \'ghi78910\', \'abc123cvz\']

我想找到列表中的子字符串并将其分组到一个元组列表中,其中元组的第一个元素是子字符串,第二个元素是包含子字符串的较大字符串。预期输出如下

[(\'abc123\', \'abc123456\'), (\'abc123\', \'abc12355\'), (\'abc123\', \'abc123cvz\'), (\'def456\', \'def4567\'), (\'ghi789\', \'ghi78910\')]

我编写了以下代码,可以达到预期的结果

substring_superstring_list = []
for sub in mylist:
   substring_superstring_pair = [(sub, s) for s in mylist if sub in s and s != sub]
   if substring_superstring_pair:
       substring_superstring_list.append(substring_superstring_pair)

flat_list = [item for sublist in substring_superstring_list for item in sublist]

有没有更有效的方法来做到这一点?我最终需要遍历包含 80k 字符串的列表并执行上述操作。我感谢任何建议/帮助

  • 你可能想创建一个trie tree
  • 如果您首先按升序对“mylist”进行排序(由于 C 实现,这很快),您可以确定 sub 的所有超字符串都在列​​表中的 sub 之后并且在任何比 sub 短的条目之前或第一个 \"len(sub)\" 字符不等于 sub。

标签: python list substring


【解决方案1】:

结合 cmets 和 @ZabielskiGrabriel's answer 中的建议,您可以通过首先对列表进行排序,然后将排序列表中的每个元素与列表理解中紧随其后的元素进行比较:

my_list = sorted(my_list)
[(x, y) for i, x in enumerate(my_list, 1) for y in my_list[i:] if x in y]

基准(附带提供的测试列表):

%timeit op(my_list)
%timeit zabiel(my_list)
%timeit nin17(my_list)

输出:

3.92 µs ± 31 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
2.76 µs ± 34.6 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
2.25 µs ± 7.75 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

【讨论】:

    【解决方案2】:

    明天我将尝试使用 map、reduce 和 filter 的另一种方法。也在这里你可以找到一个很好的教程:


    my_list = ['abc123', 'abc123456', 'abc12355', 'def456', 'ghi789', 'def4567', 'ghi78910', 'abc123cvz']
    
    output = []
    for x in my_list:
        for y in my_list:
            if x in y and x != y:
                output.append((x, y))
    print(output)
    

    【讨论】:

    • 顺便说一句,80k 的项目对 python 来说应该不是问题
    【解决方案3】:

    一种更有效的方法是使用多处理——取决于你有多少内核——在我的 8 核 PC 上,它的速度要快 10-15 倍。这很容易做到,只需将第一个for 循环更改为map 并使用multiprocessing.Pool

        global find_sub2
        def find_sub2(sub):
            sub_pair = [(sub, s) for s in mylist if sub in s and s != sub]
            if sub_pair:
                return sub_pair
            else:
                return []
        pool = multiprocessing.Pool(processes=16)
        substring_superstring_list = pool.map(find_sub2, mylist)
        pool.close()
        flat_list = [item for sublist in substring_superstring_list for item in sublist]
    

    我将某些方法的时间与随机大小为 10-200 的 20000 个随机字符串列表进行了比较:

    ['original', '31.684 seconds']
    ['traditional_loops', '63.874 seconds']
    ['two_for_loops', '32.22 seconds']
    ['with_map', '31.778 seconds']
    ['map_with_multiprocessing', '3.025 seconds']
    

    这里的代码:

    from tqdm import tqdm
    import multiprocessing
    import random
    import time
    
    ALLOWED_CHARS = 'abcdeghijklmn'
    NUMBER_OF_STRINGS = 20000
    MIN_STR_LENGTH = 10
    MAX_STR_LENGTH = 100
    
    def random_string_generator(str_size, allowed_chars=ALLOWED_CHARS):
        return ''.join(random.choice(allowed_chars) for _ in range(str_size))
    
    
    print('Creating random strings')
    mylist = [random_string_generator(random.randint(MIN_STR_LENGTH, MAX_STR_LENGTH)) for _ in tqdm(range(NUMBER_OF_STRINGS))]
    
    
    def original():
        substring_superstring_list = []
        for sub in tqdm(mylist):
            sub_pair = [(sub, s) for s in mylist if sub in s and s != sub]
            if sub_pair:
                substring_superstring_list.append(sub_pair)
        return [item for sublist in substring_superstring_list for item in sublist]
    
    
    def traditional_loops():
        output = []
        for i in tqdm(range(len(mylist))):
            for j in range(len(mylist)):
                if i != j and mylist[i] in mylist[j]:
                    output.append((mylist[i], mylist[j]))
        return output
    
    
    def two_for_loops():
        flat_list = []
        for x in tqdm(mylist):
            for y in mylist:
                if x in y and x != y:
                    flat_list.append((x, y))
        return flat_list
    
    
    def with_map():
        def find_sub(sub):
            sub_pair = [(sub, s) for s in mylist if sub in s and s != sub]
            if sub_pair:
                return sub_pair
            else:
                return []
        substring_superstring_list = map(find_sub, tqdm(mylist))
        return [item for sublist in substring_superstring_list for item in sublist]
    
    
    def map_with_multiprocessing():
        global find_sub2
        def find_sub2(sub):
            sub_pair = [(sub, s) for s in mylist if sub in s and s != sub]
            if sub_pair:
                return sub_pair
            else:
                return []
        pool = multiprocessing.Pool(processes=16)
        substring_superstring_list = pool.map(find_sub2, tqdm(mylist))
        pool.close()
        return [item for sublist in substring_superstring_list for item in sublist]
    
    
    methods = [original, traditional_loops, two_for_loops, with_map, map_with_multiprocessing]
    results = []
    for fun in methods:
        print()
        print(f'Start testing {fun.__name__}')
        start = time.time()
        flat_list = fun()
        #print(flat_list)
        end = time.time()
        result = [fun.__name__, f'{int(1000 * (end - start)) / 1000.} seconds', flat_list]
        results.append(result)
    
    solution = (set(results[0][2]), len(results[0][2]))
    print()
    for i in results:
        print(f'{i[:2]} Solution is correct? {solution == (set(i[2]), len(i[2]))}')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-29
      • 2021-07-03
      • 2012-05-16
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      相关资源
      最近更新 更多