在列表中查找特定的子列表

时间:2014-07-03 08:59:20

标签: python algorithm list

我们说我们有以下列表:

sequence = ['2', '4', '1', '2', '3', '4', '2', '4', '2', '4', '4']
#indices     0    1    2    3    4    5    6    7    8    9    10

接下来,我们有以下列表:

key_list = ['2', '2', '4']

现在,我想从sequence中提取所有可能的子列表,保留keylist的顺序,即其索引。

让我举例解释。因此,对于sequence,保留key_list顺序的所有可能的索引子列表都是:

[0, 3, 5]
[0, 3, 7]
[0, 3, 9]
[0, 3, 10]

[0, 6, 7]
[0, 6, 9]
[0, 6, 10]

[0, 8, 9]
[0, 8, 10]

[3, 6, 7]
[3, 6, 9]
[3, 6, 10]

[3, 8, 9]
[3, 8, 10]

[6, 8, 9]
[6, 8, 10]

有什么建议吗?

编辑:我正在使用一个大数据集,我必须为文件的每一行执行此操作,所以我正在寻找一种非常优化的方法来实现这一点,避免使用暴力方法(使所有可能的组合序列)

P.S。我不知道问题的标题是否合适,如果你有更好的标题,请随时更改。

6 个答案:

答案 0 :(得分:6)

您可以使用itertools.combinations。在combinations()上使用enumerate(sequence)(使用r=len(key_list))从列表中获取所有r长度组合,并且由于enumerate()同时返回索引和项目,我们可以轻松获取这里的指数:

>>> from itertools import combinations               
>>> for c in combinations(enumerate(sequence), len(key_list)):
    indices, data = zip(*c)
    if list(data) == key_list:
        print indices
...         
(0, 3, 5)
(0, 3, 7)
(0, 3, 9)
(0, 3, 10)
(0, 6, 7)
(0, 6, 9)
(0, 6, 10)
(0, 8, 9)
(0, 8, 10)
(3, 6, 7)
(3, 6, 9)
(3, 6, 10)
(3, 8, 9)
(3, 8, 10)
(6, 8, 9)
(6, 8, 10)

答案 1 :(得分:1)

它可能需要一些优化,可能是一个比列表更好的结构,以避免我现在正在做的愚蠢的复制和插入,但我认为这应该是最复杂的len(sequence)^2的技巧(虽然不确定复杂性。

sequence = ['2', '4', '1', '2', '3', '4', '2', '4', '2', '4', '4']
key_list = ['2', '2', '4']

sub_lists = []
final_sub_lists = set()
len_key_list = len(key_list)

for index, value in enumerate(sequence):
    for sub_list in sub_lists:
        len_sub_list = len(sub_list)
        # Test if current value can continue the current sub list
        if len_sub_list < len_key_list and key_list[len_sub_list] == value:
            if len_sub_list == len_key_list - 1:
                # We have found a complete sub list
                final_sub_lists.add(tuple(sub_list + [index]))
            else:
                # We copy the current sub list to be sure not miss any sub lists
                # like for instance (6, 8, 9) and (6, 8, 10).
                sub_lists.insert(0, sub_list[:])
                sub_list.append(index)
    if key_list[0] == value:
        # Start a new sub list
        sub_lists.append([index])

print sorted(final_sub_lists)

说明:sub_lists是一个列表,其中包含到目前为止已匹配的索引。当sub_list匹配key_list的所有值时,它会附加到集final_sub_lists

它没有经过全面测试,因此请随意纠正或指出优化!

答案 2 :(得分:1)

这是一种递归方法。

我查找第一个键的每个索引。然后我使用相同的函数来查找以下键并加入所有索引...

def indexLists(sequence, key_list, seq_start=0, key_start=0):
     """
         seq_start - where I start looking up in sequence
         key_start - which key I am looking up: key = key_list[key_start]
     """
     keyIndexes = []

     # I look up all indices of key_list[key_start] that are higher than seq_start
     while True:

         try:
             keyIndexes.append(
                  sequence.index(
                     key_list[key_start],# what I want to look up
                     keyIndexes[-1]+1 if keyIndexes else seq_start # starting after the last entry or seq_start
                  )
              )
         except:
             break # if there is an error, the are no more indices

     # if there are more entries in key_list
     if key_start+1 < len(key_list):
         # I look up the possible indexes of the following key(s) and combine them
         return [(keyIndex,)+nextKeys  for keyIndex in keyIndexes for nextKeys in indexLists(sequence, key_list, keyIndex+1, key_start+1)]
     else:
         # for the last key in key_list i just return all possible keyIndexes as 1-tuples
         return [(keyIndex, ) for keyIndex in keyIndexes]

示例:

sequence = ['2', '4', '1', '2', '3', '4', '2', '4', '2', '4', '4']
key_list = ['2', '2', '4']

indexLists(sequence, key_list)
Out[37]: 
[(0, 3, 5),
 (0, 3, 7),
 (0, 3, 9),
 (0, 3, 10),
 (0, 6, 7),
 (0, 6, 9),
 (0, 6, 10),
 (0, 8, 9),
 (0, 8, 10),
 (3, 6, 7),
 (3, 6, 9),
 (3, 6, 10),
 (3, 8, 9),
 (3, 8, 10),
 (6, 8, 9),
 (6, 8, 10)]

答案 3 :(得分:1)

这扩展了Sebastiens的回答,认为你不需要任何不在key_list中的序列成员(现在是key_tuple),只要你保留原来的索引是什么`左:

>>> from itertools import combinations
>>> sequence = ['2', '4', '1', '2', '3', '4', '2', '4', '2', '4', '4']
>>> key_tuple = ('2', '2', '4')
>>> keys = set(key_tuple)
>>> seq = [(indx, val) for indx, val in enumerate(sequence) if val in keys]
>>> seq
[(0, '2'), (1, '4'), (3, '2'), (5, '4'), (6, '2'), (7, '4'), (8, '2'), (9, '4'), (10, '4')]
>>> answer = []
>>> for c in combinations(seq, len(key_tuple)):
...     indxs, vals = zip(*c)
...     if vals == key_tuple:
...         answer.append(indxs)
... 
>>> answer
[(0, 3, 5), (0, 3, 7), (0, 3, 9), (0, 3, 10), (0, 6, 7), (0, 6, 9), (0, 6, 10), (0, 8, 9), (0, 8, 10), (3, 6, 7), (3, 6, 9), (3, 6, 10), (3, 8, 9), (3, 8, 10), (6, 8, 9), (6, 8, 10)]
>>> 

答案 4 :(得分:1)

这是伪装的Longest Common Subsequence问题。与通常的公式的唯一区别在于,您需要位置而不是字符本身,并且您认为key_list序列完整地显示为sequence的子序列,而LCS问题没有做出这个假设。

LCS问题与最佳比对两个序列(例如DNA序列)的问题密切相关,可以使用Needleman-Wunsch动态编程算法在O(n ^ 2)时间内求解,但仅限于此为您提供一个解决方案;在最坏的情况下,可能需要指数长时间来枚举所有这些(考虑在大k的2k 1s列表中寻找k 1列表的情况;有(2k选择k)答案)。也就是说,从DP矩阵获取这些位置同样容易,并且直接枚举所有解决方案而不是单个解决方案:当您回溯DP矩阵时,无论何时遇到两个或所有的单元格三个边缘具有(相等)最大分数(而不是只有一个边缘是唯一的最大分数),递归处理所有这些而不是挑选任意一个。

顺便提一下,如果key_listsequence中的任何位置都不显示为子序列,那么LCS算法将找到所有“最近”匹配的位置 - 缺少最少字符的位置。这可能对您有用,也可能没用。

答案 5 :(得分:1)

我的第二个答案发现序列中所有键的索引然后使用递归迭代器(Python 2.x / 3.x所以我没有使用{{ 1}},找到可能的索引组合:

yield from

输出:

from collections import defaultdict

sequence = ['2', '4', '1', '2', '3', '4', '2', '4', '2', '4', '4']
key_list = ['2', '2', '4']
keys = set(key_list)

key_indices = defaultdict(list)
for indx, val in enumerate(sequence):
    if val in keys:
        key_indices[val].append(indx)
print('key_indices =', key_indices)

def expander(keysleft, indices, sofar=None):
    #print('  keysleft, sofar =', keysleft, sofar )
    if sofar is None :
        sofar = []
    if sofar == []:
        indxleft = -1
    else:
        indxleft = sofar[-1]
    if keysleft:
        keyval, keyrest = keysleft[0], keysleft[1:]
        for keyindx in indices[keyval]:
            if keyindx > indxleft:
                if not keyrest:
                    # No more to do so
                    yield tuple(sofar + [keyindx])
                else:
                    for x in expander(keyrest, indices, sofar + [keyindx]):
                        yield x

ans = list(expander(key_list, dict(key_indices)))
print(ans)