假设我有以下列表列表:
x = [[1, 2, 3, 4, 5, 6, 7], # sequence 1
[6, 5, 10, 11], # sequence 2
[9, 8, 2, 3, 4, 5], # sequence 3
[12, 12, 6, 5], # sequence 4
[5, 8, 3, 4, 2], # sequence 5
[1, 5], # sequence 6
[2, 8, 8, 3, 5, 9, 1, 4, 12, 5, 6], # sequence 7
[7, 1, 7, 3, 4, 1, 2], # sequence 8
[9, 4, 12, 12, 6, 5, 1], # sequence 9
]
本质上,对于列表中任何位置包含目标编号5
(即target=5
)的任何列表,长度为{{1}的最常见N=2
个子序列是什么}}?
因此,条件是:
M=4
,则我们将完全忽略该列表target
,则我们将完全忽略列表M
,但M
不在target
位置,则我们将其忽略(但如果Mth
位于{{ 1}}位置)target
长于Mth
,并且L
位于M
位置target
i = M + 1 {{1 }} i = M + 2 i=M
i = L (or
M position, or
目标在子序列中处于最终位置因此,使用列表列表示例,我们将计算以下子序列:
position, ...,
当然,我们想要的是频率最高的position) then we count the subsequence of length
子序列。因此,按计数,where
和subseqs = [[2, 3, 4, 5], # taken from sequence 1
[2, 3, 4, 5], # taken from sequence 3
[12, 12, 6, 5], # taken from sequence 4
[8, 8, 3, 5], # taken from sequence 7
[1, 4, 12, 5], # taken from sequence 7
[12, 12, 6, 5], # taken from sequence 9
]
是最常见的两个序列。如果N=2
,则将返回所有子序列([2, 3, 4, 5]
),因为并列第三个。
这是超级简化的,但实际上是我的实际列表
[12, 12, 6, 5]
和N=3
可以小到1或大到100 我的问题是:
subseqs
和N
始终小于100,是否有一种有效的数据结构可以进行快速查询?M
和N
的各种组合执行这种分析?答案 0 :(得分:0)
这是一个基于generalized suffix tree结构的想法。您的列表列表可以看作是一个字符串列表,其中字母将由整数组成(因此,带有您提供的信息的字母中约有10k个字符)。
广义后缀树的构建是在不带字符串长度的线性时间内完成的,因此这不成问题,因为在任何情况下,您都必须遍历列表 。
首先,将所有字符串存储在后缀树中。这需要对结构进行2次小的改动。
您需要对某个后缀的出现次数进行计数,因为您的目标是最终找到尊重某些属性的最常见子序列。
然后,您还希望有一个来自(i, d)
的查找表(其中i
是您要查找的整数,目标,而d
是您树中的深度) ,即M
到后缀链接的节点集的集合,该节点集标记有深度为{{的字母(由字母组成,但不是由字符组成,而是由整数组成)” 1}}。可以通过遍历后缀链接(BFS或DFS)来构建此查找表。您甚至可以只存储对应于最高计数器值的节点。
从那里开始,对于某些查询i
,您将首先在查找表中查找,然后在树中找到计数器值最高的节点。这将对应于列表中最经常遇到的“后缀”(或子序列)。
实现非常复杂,因为广义后缀树根本不是一个琐碎的结构,并且正确地对其进行修改后的实现并不是一件容易的事。但是我认为这将允许非常高效的查询时间。
对于后缀树的实现,我建议您仅阅读原始论文,直到您对这些论文有深入而真实的了解(例如this或that,sc * -h * b可以成为您的朋友),而不是在线上的“说明”,因为它们充满了近似和错误(即使this post也会帮助您获得第一个想法,但如果您的目标是在某个时候会误导您是为了实现正确的版本。
答案 1 :(得分:0)
要回答第一个问题:您可以将所有列表放入数组中,通过扩展零来固定长度,这样数组就可以使用。来自答案here
x = [[1, 2, 3, 4, 5, 6, 7], # sequence 1
[6, 5, 10, 11], # sequence 2
[9, 8, 2, 3, 4, 5], # sequence 3
[12, 12, 6, 5], # sequence 4
[5, 8, 3, 4, 2], # sequence 5
[1, 5], # sequence 6
[2, 8, 8, 3, 5, 9, 1, 4, 12, 5, 6], # sequence 7
[7, 1, 7, 3, 4, 1, 2], # sequence 8
[9, 4, 12, 12, 6, 5, 1], # sequence 9
]
lens = np.fromiter(map(len, x), np.int)
n1, n2 = len(lens), lens.max()
arr = np.zeros((n1, n2), dtype=np.int)
mask = np.arange(n2) < lens[:,None]
arr[mask] = np.concatenate(x)
arr
>> [[ 1 2 3 4 5 6 7 0 0 0 0]
[ 6 5 10 11 0 0 0 0 0 0 0]
[ 9 8 2 3 4 5 0 0 0 0 0]
[12 12 6 5 0 0 0 0 0 0 0]
[ 5 8 3 4 2 0 0 0 0 0 0]
[ 1 5 0 0 0 0 0 0 0 0 0]
[ 2 8 8 3 5 9 1 4 12 5 6]
[ 7 1 7 3 4 1 2 0 0 0 0]
[ 9 4 12 12 6 5 1 0 0 0 0]]
第二个问题:使用np.where
查找符合您条件的不同职位。然后,您可以通过添加尺寸以包含5
和前面的4个元素来广播行和列的索引:
M = 4
N = 5
r, c = np.where(arr[:, M-1:]==N)
arr[r[:,None], (c[:,None] + np.arange(M))]
>>array([[ 2, 3, 4, 5],
[ 2, 3, 4, 5],
[12, 12, 6, 5],
[ 8, 8, 3, 5],
[ 1, 4, 12, 5],
[12, 12, 6, 5]])
答案 2 :(得分:0)
您的问题分为两部分:
要生成所需的子序列,可以使用生成器来帮助您:
def gen_m(lst, m, val):
'''
lst = sub_list to parse
m = length required
val = target value
'''
found = 0 # starts with 0 index
for i in range(lst[m-1:].count(val)): # repeat by the count of val
found = lst.index(val, found) + 1 # set and find the next index of val
yield tuple(lst[found-m: found]) # yield the sliced sub_list of m length as a tuple
然后,使用另一个生成器,您可以创建子列表的Counter
:
from collections import Counter
target = 5
req_len = 4
# the yielded sub_lists need to be tuples to be hashable for the Counter
counter = Counter(sub_tup for lst in x for sub_tup in gen_m(lst, req_len, target))
然后,创建一个生成器以检查计数器对象以返回所需的N
计数:
req_N = 2
def gen_common(counter, n):
s = set()
for i, (item, count) in enumerate(counter.most_common()):
if i < n or count in s:
yield item
else:
return
s.add(count)
result = list(gen_common(counter, req_N))
结果,其中N == 2
:
[[2, 3, 4, 5], [12, 12, 6, 5]]
结果,其中N == 3
:
[[2, 3, 4, 5], [12, 12, 6, 5], [8, 8, 3, 5], [1, 4, 12, 5]]
使用更大的样本:
x = [[1, 2, 3, 4, 5, 6, 7],
[6, 5, 10, 11],
[9, 8, 2, 3, 4, 5],
[12, 12, 6, 5],
[5, 8, 3, 4, 2],
[1, 5],
[2, 8, 8, 3, 5, 9, 1, 4, 12, 5, 6],
[7, 1, 7, 3, 4, 1, 2],
[9, 4, 12, 12, 6, 5, 1],
[9, 4, 12, 12, 6, 5, 1],
[9, 4, 2, 3, 4, 5, 1],
[9, 4, 8, 8, 3, 5, 1],
[9, 4, 7, 8, 9, 5, 1],
[9, 4, 1, 2, 2, 5, 1],
[9, 4, 12, 12, 6, 5, 1],
[9, 4, 12, 12, 6, 5, 1],
[9, 4, 1, 4, 12, 5],
[9, 1, 4, 12, 5, 1]
]
Counter
现在所在的位置:
Counter({(12, 12, 6, 5): 5, (2, 3, 4, 5): 3, (1, 4, 12, 5): 3, (8, 8, 3, 5): 2, (7, 8, 9, 5): 1, (1, 2, 2, 5): 1})
您可以获得以下结果:
for i in range(6):
# testing req_N from 0 to 5
list(gen_common(c, i))
# req_N = 0: []
# req_N = 1: [(12, 12, 6, 5)]
# req_N = 2: [(12, 12, 6, 5), (2, 3, 4, 5), (1, 4, 12, 5)]
# req_N = 3: [(12, 12, 6, 5), (2, 3, 4, 5), (1, 4, 12, 5)]
# req_N = 4: [(12, 12, 6, 5), (2, 3, 4, 5), (1, 4, 12, 5), (8, 8, 3, 5)]
# req_N = 5: [(12, 12, 6, 5), (2, 3, 4, 5), (1, 4, 12, 5), (8, 8, 3, 5), (7, 8, 9, 5), (1, 2, 2, 5)]
答案 3 :(得分:0)
由于不只有N,M和目标,所以我假设存在带有列表的列表块。这是一种O(N + M)时间复杂度的方法(其中N是块中列表的数量,M是元素总数):
def get_seq(x, M, target):
index_for_length_m = M - 1
for v in [l for l in x if len(l) >= M]:
for i in [i for i, v in enumerate(v[index_for_length_m:], start=index_for_length_m) if v == target]:
# convert to str to be hashable
yield str(v[i - index_for_length_m : i + 1])
def process_chunk(x, M, N, target):
return Counter(get_seq(x, M, target)).most_common(N)
以您的示例为例:
process_chunk(x, M, 2, target)
输出:
[('[2, 3, 4, 5]', 2), ('[12, 12, 6, 5]', 2)]
表演:
%timeit process_chunk(x, M, 2, target)
# 25 µs ± 713 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)