我想检查一个子列表是否存在于另一个(更大的)列表中,元素的顺序完全相同。我也希望它允许通配符。例如,我有以下列表:
>>> my_lists
[[0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 2, 2],
[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 1, 0, 2, 1, 2, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 1, 2, 1, 2, 2, 1, 1],
[0, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 2, 2, 1, 1, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 2, 1, 1, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
Run Code Online (Sandbox Code Playgroud)
和子列表:[0, 0, 0, 1]. 如果我想找到哪些列表包含这个确切的子列表,我可以做(取自这里):
def my_func(_list, sub_list):
n = len(sub_list)
return any((sub_list== _list[i:i+n]) for i in range(len(_list)-n+1))
for l in my_lists:
if my_func(l, [0, 0, 0, 1]):
print(l)
Run Code Online (Sandbox Code Playgroud)
...这基本上使所有可能的子列表与 长度相同sub_list,并检查是否有任何相等。我会得到以下输出,因为这些列表包含[0, 0, 0, 1]:
[1, 1, 1, 1, 0, 2, 1, 2, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 1, 1, 2, 1, 2, 2, 1, 1]
[1, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0]
[0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Run Code Online (Sandbox Code Playgroud)
现在我还想添加通配符,这意味着我可以给子列表通配符元素。例如,现在我想找到 sublist [*, *, 0, 0, 0, 1, *]。这里的星号表示对于这些元素,值可以是列表中的任何内容。但是对于那些星号,必须有一个值。子列表[*, *, 0, 0, 0, 1, *]现在将输出:
[1, 1, 1, 1, 0, 2, 1, 2, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 0]
[1, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0]
Run Code Online (Sandbox Code Playgroud)
请注意, now[0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]不包括在内,因为此列表在[0, 0, 0, 1]序列开始之前没有两个值。这同样适用于[0, 0, 0, 0, 1, 1, 2, 1, 2, 2, 1, 1],也没有序列前两个值。请注意,星号可以是诸如 np.nan 之类的任何内容。
我将如何扩展上述代码以允许使用通配符?
all一种方法是在检查子列表和if跳过星号时使用:
def my_func(a_list, sub_list):
n = len(sub_list)
# list-level comparison is now via element-wise
return any(all(sub_item == chunk_item
for sub_item, chunk_item in zip(sub_list, a_list[i:i+n])
if not np.isnan(sub_item)) # "is_not_asterisk" condition
for i in range(len(a_list)-n+1))
Run Code Online (Sandbox Code Playgroud)
我在其中not np.isnan(...)用作问题中提到的星号条件;但它可能有很多事情:例如,如果星号实际上"*"在子列表中,那么那里的条件将更改为if sub_item != "*"。
带有np.nan星号的示例:
for a_list in my_lists:
if my_func(a_list, [np.nan, np.nan, 0, 0, 0, 1, np.nan]):
print(a_list)
Run Code Online (Sandbox Code Playgroud)
给出
[1, 1, 1, 1, 0, 2, 1, 2, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 0]
[1, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0]
Run Code Online (Sandbox Code Playgroud)
all如果迭代器为空则返回True,因此如果传递了全星号子列表,它将返回True所有候选者(只要它们的长度允许,这any将处理,因为any空迭代器是False!)。