使用Python如何通过有序子集匹配[[..],[..],..]
来减少列表列表?
在此问题的上下文中,如果M
包含M
的所有成员,则列表L是列表L
的 子集 },并以相同的顺序。例如,列表[1,2]是列表[1,2,3]的子集,但不是列表[2,1,3]的子集。
示例输入:
a. [[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
b. [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [1], [1, 2, 3, 4], [1, 2], [17, 18, 19, 22, 41, 48], [2, 3], [1, 2, 3], [50, 69], [1, 2, 3], [2, 3, 21], [1, 2, 3], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
预期结果:
a. [[1, 2, 4, 8], [2, 3, 21], [1, 2, 3, 4, 5, 6, 7]]
b. [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [17, 18, 19, 22, 41, 48], [50, 69], [2, 3, 21], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
更多示例:
L = [[1, 2, 3, 4, 5, 6, 7], [1, 2, 5, 6]]
- 不减少
L = [[1, 2, 3, 4, 5, 6, 7],
,[1, 2, 3]
[1, 2, 4, 8]]
- 是减少
L = [[1, 2, 3, 4, 5, 6, 7], [7, 6, 5, 4, 3, 2, 1]]
- 不减少
(很抱歉导致与不正确的数据集混淆。)
答案 0 :(得分:8)
这可以简化,但是:
l = [[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
l2 = l[:]
for m in l:
for n in l:
if set(m).issubset(set(n)) and m != n:
l2.remove(m)
break
print l2
[[1, 2, 4, 8], [2, 3, 21], [1, 2, 3, 4, 5, 6, 7]]
答案 1 :(得分:4)
此代码应该具有相当的内存效率。除了存储您的初始列表列表之外,此代码使用可忽略不计的额外内存(不会创建临时集或列表副本)。
def is_subset(needle,haystack):
""" Check if needle is ordered subset of haystack in O(n) """
if len(haystack) < len(needle): return False
index = 0
for element in needle:
try:
index = haystack.index(element, index) + 1
except ValueError:
return False
else:
return True
def filter_subsets(lists):
""" Given list of lists, return new list of lists without subsets """
for needle in lists:
if not any(is_subset(needle, haystack) for haystack in lists
if needle is not haystack):
yield needle
my_lists = [[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3],
[2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
print list(filter_subsets(my_lists))
>>> [[1, 2, 4, 8], [2, 3, 21], [1, 2, 3, 4, 5, 6, 7]]
而且,只是为了好玩,一个单行:
def filter_list(L):
return [x for x in L if not any(set(x)<=set(y) for y in L if x is not y)]
答案 2 :(得分:1)
如果列表不是任何其他列表的子集,则列表是超级列表。如果可以按顺序在另一个列表中找到列表的每个元素,则它是另一个列表的子集。
这是我的代码:
def is_sublist_of_any_list(cand, lists):
# Compare candidate to a single list
def is_sublist_of_list(cand, target):
try:
i = 0
for c in cand:
i = 1 + target.index(c, i)
return True
except ValueError:
return False
# See if candidate matches any other list
return any(is_sublist_of_list(cand, target) for target in lists if len(cand) <= len(target))
# Compare candidates to all other lists
def super_lists(lists):
return [cand for i, cand in enumerate(lists) if not is_sublist_of_any_list(cand, lists[:i] + lists[i+1:])]
if __name__ == '__main__':
lists = [[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
superlists = super_lists(lists)
print superlists
结果如下:
[[1, 2, 4, 8], [2, 3, 21], [1, 2, 3, 4, 5, 6, 7]]
编辑:以后数据集的结果。
>>> lists = [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [1], [1, 2, 3, 4], [1, 2], [17,
18, 19, 22, 41, 48], [2, 3], [1, 2, 3], [50, 69], [1, 2, 3], [2, 3, 21], [1, 2,
3], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
>>> superlists = super_lists(lists)
>>> expected = [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [17, 18, 19, 22, 41, 48], [5
0, 69], [2, 3, 21], [1, 2, 4, 8]]
>>> assert(superlists == expected)
>>> print superlists
[[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [17, 18, 19, 22, 41, 48], [50, 69], [2, 3,
21], [1, 2, 4, 8]]
答案 3 :(得分:0)
编辑:我真的需要提高阅读理解能力。以下是实际问题的答案。它利用了“A is super of B
”暗示“len(A) > len(B) or A == B
”的事实。
def advance_to(it, value):
"""Advances an iterator until it matches the given value. Returns False
if not found."""
for item in it:
if item == value:
return True
return False
def has_supersequence(seq, super_sequences):
"""Checks if the given sequence has a supersequence in the list of
supersequences."""
candidates = map(iter, super_sequences)
for next_item in seq:
candidates = [seq for seq in candidates if advance_to(seq, next_item)]
return len(candidates) > 0
def find_supersequences(sequences):
"""Finds the supersequences in the given list of sequences.
Sequence A is a supersequence of sequence B if B can be created by removing
items from A."""
super_seqs = []
for candidate in sorted(sequences, key=len, reverse=True):
if not has_supersequence(candidate, super_seqs):
super_seqs.append(candidate)
return super_seqs
print(find_supersequences([[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3],
[2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]))
#Output: [[1, 2, 3, 4, 5, 6, 7], [1, 2, 4, 8], [2, 3, 21]]
如果您还需要保留序列的原始顺序,那么find_supersequences()
函数需要跟踪序列的位置并在之后对输出进行排序。
答案 4 :(得分:0)
list0=[[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
for list1 in list0[:]:
for list2 in list0:
if list2!=list1:
len1=len(list1)
c=0
for n in list2:
if n==list1[c]:
c+=1
if c==len1:
list0.remove(list1)
break
使用它的副本过滤list0。如果预期结果与原始大小大致相同,那么这是很好的,只有少数“子集”可以删除。
如果预期结果很小并且原始文件很大,那么您可能更喜欢这个更友好的内存,因为它不会复制原始列表。
list0=[[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
result=[]
for list1 in list0:
subset=False
for list2 in list0:
if list2!=list1:
len1=len(list1)
c=0
for n in list2:
if n==list1[c]:
c+=1
if c==len1:
subset=True
break
if subset:
break
if not subset:
result.append(list1)
答案 5 :(得分:0)
这似乎有效:
original=[[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
target=[[1, 2, 4, 8], [2, 3, 21], [1, 2, 3, 4, 5, 6, 7]]
class SetAndList:
def __init__(self,aList):
self.list=aList
self.set=set(aList)
self.isUnique=True
def compare(self,aList):
s=set(aList)
if self.set.issubset(s):
#print self.list,'superceded by',aList
self.isUnique=False
def listReduce(lists):
temp=[]
for l in lists:
for t in temp:
t.compare(l)
temp.append( SetAndList(l) )
return [t.list for t in temp if t.isUnique]
print listReduce(original)
print target
这将打印计算列表和目标以进行视觉比较。
在比较方法中取消注释打印行,以查看各种列表如何被取代。
使用python 2.6.2进行测试
答案 6 :(得分:0)
我实施了一个不同的issubseq
,因为你的[1, 2, 4, 5, 6]
并不是[1, 2, 3, 4, 5, 6, 7]
的后续序列(除了痛苦的慢)。我提出的解决方案看起来像这样:
def is_subseq(a, b):
if len(a) > len(b): return False
start = 0
for el in a:
while start < len(b):
if el == b[start]:
break
start = start + 1
else:
return False
return True
def filter_partial_matches(sets):
return [s for s in sets if all([not(is_subseq(s, ss)) for ss in sets if s != ss])]
一个简单的测试用例,给出您的输入和输出:
>>> test = [[1, 2, 4, 8], [1, 2, 4, 5, 6], [1, 2, 3], [2, 3, 21], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6, 7]]
>>> another_test = [[1, 2, 3, 4], [2, 4, 3], [3, 4, 5]]
>>> filter_partial_matches(test)
[[1, 2, 4, 8], [2, 3, 21], [1, 2, 3, 4, 5, 6, 7]]
>>> filter_partial_matches(another_test)
[[1, 2, 3, 4], [2, 4, 3], [3, 4, 5]]
希望它有所帮助!
答案 7 :(得分:0)
新测试案例后的精确答案:
original= [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [1], [1, 2, 3, 4], [1, 2], [17, 18, 19, 22, 41, 48], [2, 3], [1, 2, 3], [50, 69], [1, 2, 3], [2, 3, 21], [1, 2, 3], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
class SetAndList:
def __init__(self,aList):
self.list=aList
self.set=set(aList)
self.isUnique=True
def compare(self,other):
if self.set.issubset(other.set):
#print self.list,'superceded by',other.list
self.isUnique=False
def listReduce(lists):
temp=[]
for l in lists:
s=SetAndList(l)
for t in temp:
t.compare(s)
s.compare(t)
temp.append( s )
temp=[t for t in temp if t.isUnique]
return [t.list for t in temp if t.isUnique]
print listReduce(original)
您没有提供所需的输出,但我猜这是正确的,因为[1,2,3]
没有出现在输出中。
答案 8 :(得分:0)
感谢所有建议解决方案和处理我有时错误的数据集的人。使用 @hughdbrown 解决方案,我将其修改为我想要的内容:
修改是在目标上使用滑动窗口以确保找到子集序列。我想我应该使用比'Set'更合适的词来描述我的问题。
def is_sublist_of_any_list(cand, lists):
# Compare candidate to a single list
def is_sublist_of_list(cand, target):
try:
i = 0
try:
start = target.index(cand[0])
except:
return False
while start < (len(target) + len(cand)) - start:
if cand == target[start:len(cand)]:
return True
else:
start = target.index(cand[0], start + 1)
except ValueError:
return False
# See if candidate matches any other list
return any(is_sublist_of_list(cand, target) for target in lists if len(cand) <= len(target))
# Compare candidates to all other lists
def super_lists(lists):
a = [cand for i, cand in enumerate(lists) if not is_sublist_of_any_list(cand, lists[:i] + lists[i+1:])]
return a
lists = [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [1], [1, 2, 3, 4], [1, 2], [17, 18, 19, 22, 41, 48], [2, 3], [1, 2, 3], [50, 69], [1, 2, 3], [2, 3, 21], [1, 2, 3], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
expect = [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [17, 18, 19, 22, 41, 48], [50, 69], [2, 3, 21], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
def test():
out = super_lists(list(lists))
print "In : ", lists
print "Out : ", out
assert (out == expect)
结果:
In : [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [1], [1, 2, 3, 4], [1, 2], [17, 18, 19, 22, 41, 48], [2, 3], [1, 2, 3], [50, 69], [1, 2, 3], [2, 3, 21], [1, 2, 3], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
Out : [[2, 16, 17], [1, 2, 3, 4, 5, 6, 7], [17, 18, 19, 22, 41, 48], [50, 69], [2, 3, 21], [1, 2, 4, 8], [1, 2, 4, 5, 6]]
答案 9 :(得分:0)
所以你真正想要的是知道一个列表是否是一个子字符串,可以说是另一个,所有匹配元素都是连续的。下面是将候选列表和目标列表转换为逗号分隔字符串并执行子字符串比较以查看候选项是否出现在目标列表中的代码
def is_sublist_of_any_list(cand, lists):
def comma_list(l):
return "," + ",".join(str(x) for x in l) + ","
cand = comma_list(cand)
return any(cand in comma_list(target) for target in lists if len(cand) <= len(target))
def super_lists(lists):
return [cand for i, cand in enumerate(lists) if not is_sublist_of_any_list(cand, lists[:i] + lists[i+1:])]
函数comma_list()在列表中放置前导和尾随逗号,以确保整数是完全分隔的。否则,[1]将是[100]的子集,例如。