首页 > 解决方案 > 从两个列表(源列表和标签列表)有效地生成唯一的正负三元组

问题描述

我对 python 很陌生,我想从两个列表中生成所有唯一的三元组。所以,我有两个这样的列表:

source_list=[1,2,3,4,5,6,7,8..] # size N
labels_list=[1,1,0,2,1,3,6..] # size N

我想要三胞胎形式:

<anchor> <postive> <negative>

其中,<anchor>可以是 中的任何元素source_listpostive表示该元素应该来自与锚点相同的标签(因此,1,2 和 1,5 可以被视为三元组中的正对)并且negative's 表示它们应该来自不同的标签(因此,1,3 和 1,4 将被视为三元组的负对)。因此,正确三元组的一些示例是:

1, 2, 3
1, 2, 4
1, 2, 6

从一开始这似乎是一个N choose k问题,但我不知道如何以最小的计算成本来解决这个问题,而不是在 python 中对每个元素进行循环。

标签: python

解决方案


numpy可能有更快的方法来做到这一点,但我们可以用and很好地解决它itertools。首先,对于每个唯一的标签k,我们需要划分source_list为对应于标签k的条目(“正”部分)和不对应的条目(“负”部分)。

>>> source_list = [1,2,3,4,5,6,7,8] #... size N
>>> labels_list = [1,1,0,2,1,3,6,2] #... size N

>>> for key in set(labels_list): 
...     positive = [s for i,s in enumerate(source_list) if labels_list[i] == key] 
...     negative = [s for i,s in enumerate(source_list) if labels_list[i] != key] 
...     print(key, positive, negative) 
...                                                                                 
0 [3] [1, 2, 4, 5, 6, 7, 8]
1 [1, 2, 5] [3, 4, 6, 7, 8]
2 [4, 8] [1, 2, 3, 5, 6, 7]
3 [6] [1, 2, 3, 4, 5, 7, 8]
6 [7] [1, 2, 3, 4, 5, 6, 8]

我们可以使用 NumPy 和 masking 稍微加快速度,这基本上可以让我们避免两次执行列表推导。

>>> import numpy as np

>>> source_array = np.array([1,2,3,4,5,6,7,8])
>>> labels_array = np.array([1,1,0,2,1,3,6,2])  

>>> for key in np.unique(labels_array): 
...     mask = (labels_array == key)
...     positive = source_array[mask]
...     negative = source_array[~mask]   # "not" mask
...     print(key, positive, negative) 
...
0 [3] [1 2 4 5 6 7 8]
1 [1 2 5] [3 4 6 7 8]
2 [4 8] [1 2 3 5 6 7]
3 [6] [1 2 3 4 5 7 8]
6 [7] [1 2 3 4 5 6 8]

旁注:如果<anchor><positive>不允许代表相同的条目sources_list,那么我们需要每个正组至少有两个成员。这可能会发生在您的实际数据中,但对于此演示,我们将仅跳过带有单一阳性的案例。

现在来了itertools。我们想要来自肯定列表的 2 个条目的所有唯一排列,以及来自否定列表的另一个条目。由于结果必须是唯一的,我们可以稍微降低复杂性,从每个正面和负面列表中删除重复项。

>>> import itertools
>>> import numpy as np

>>> source_array = np.array([1,2,3,4,5,6,7,8])
>>> labels_array = np.array([1,1,0,2,1,3,6,2])  

>>> for key in np.unique(labels_array): 
...     mask = (labels_array == key)
...     positive = np.unique(source_array[mask])  # No duplicates
...     if len(positive) < 2:                     # Skip singleton positives
...         continue
...     negative = np.unique(source_array[~mask]) # No duplicates
...     print(key, positive, negative) 
...     for ((a,b),c) in itertools.product(itertools.permutations(positive, 2),
...                                        negative):
...         print(a,b,c)
...     print()

输出:

1 [1 2 5] [3 4 6 7 8]
1 2 3
1 2 4
1 2 6
1 2 7
1 2 8
1 5 3
1 5 4
1 5 6
1 5 7
1 5 8
2 1 3
2 1 4
2 1 6
2 1 7
2 1 8
2 5 3
2 5 4
2 5 6
2 5 7
2 5 8
5 1 3
5 1 4
5 1 6
5 1 7
5 1 8
5 2 3
5 2 4
5 2 6
5 2 7
5 2 8

2 [4 8] [1 2 3 5 6 7]
4 8 1
4 8 2
4 8 3
4 8 5
4 8 6
4 8 7
8 4 1
8 4 2
8 4 3
8 4 5
8 4 6
8 4 7

推荐阅读