为数组中的每个唯一元素创建布尔掩码

问题描述:

我有一个包含数字的列表。我想为此列表的每个唯一元素创建一个此列表(或数组,无关紧要)的布尔掩码。为数组中的每个唯一元素创建布尔掩码

在下面的示例中,我想创建四个长度为len(labels)的蒙版。所述第一掩模具有在True位置i,如果labels[i]==0,第二个在i位置具有True,如果labels[i]==1

我试图与熊猫和在一个循环中.isin方法做到这一点。然而,这对于我的目的来说太慢了,因为这在我的算法中被多次调用,并且标签列表可能很长,所以循环无效。我怎样才能让这个更快?

labels = [0,0,1,1,3,3,3,1,2,1,0,0] 
d = dict() 
y = pd.Series(labels) 
for i in set(labels): 
    d[i] = y.isin([i]) 

方法1

使用listset

In [989]: {x: [x==l for l in labels] for x in set(labels)} 
Out[989]: 
{0: [True, True, False, False, False, False, False, False, False, False, True, True], 
1: [False, False, True, True, False, False, False, True, False, True, False, False], 
2: [False, False, False, False, False, False, False, False, True, False, False, False], 
3: [False, False, False, False, True, True, True, False, False, False, False, False]} 

如果你想把它当作dataframe

In [994]: pd.DataFrame({x: [x==l for l in labels] for x in set(labels)}) 
Out[994]: 
     0  1  2  3 
0 True False False False 
1 True False False False 
2 False True False False 
3 False True False False 
4 False False False True 
5 False False False True 
6 False False False True 
7 False True False False 
8 False False True False 
9 False True False False 
10 True False False False 
11 True False False False 

方法2

使用pd.get_dummies,如果你反正series可以

In [997]: pd.get_dummies(y).astype(bool) 
Out[997]: 
     0  1  2  3 
0 True False False False 
1 True False False False 
2 False True False False 
3 False True False False 
4 False False False True 
5 False False False True 
6 False False False True 
7 False True False False 
8 False False True False 
9 False True False False 
10 True False False False 
11 True False False False 

基准

In [1002]: len(labels) 
Out[1002]: 12 

In [1003]: %timeit pd.get_dummies(y).astype(bool) 
1000 loops, best of 3: 476 µs per loop 

In [1004]: %timeit pd.DataFrame({x: [x==l for l in labels] for x in set(labels)}) 
1000 loops, best of 3: 580 µs per loop 

In [1005]: %timeit pd.DataFrame({x : (y == x) for x in y.unique()}) 
1000 loops, best of 3: 1.15 ms per loop 

In [1011]: len(labels) 
Out[1011]: 12000 

In [1012]: %timeit pd.get_dummies(y).astype(bool) 
1000 loops, best of 3: 875 µs per loop 

In [1013]: %timeit pd.DataFrame({x: [x==l for l in labels] for x in set(labels)}) 
100 loops, best of 3: 4.97 ms per loop 

In [1014]: %timeit pd.DataFrame({x : (y == x) for x in y.unique()}) 
1000 loops, best of 3: 1.32 ms per loop 
+0

这样写for循环比我的方式更快吗? – Merlin1896

+0

它往往很快。请阅读https://stackoverflow.com/q/30245397 – Zero

可以使用pd.Series.unique的字典解析里:

In [211]: pd.DataFrame({x : (y == x) for x in y.unique()}) 
Out[211]: 
     0  1  2  3 
0 True False False False 
1 True False False False 
2 False True False False 
3 False True False False 
4 False False False True 
5 False False False True 
6 False False False True 
7 False True False False 
8 False False True False 
9 False True False False 
10 True False False False 
11 True False False False 

列标题对应于该系列中的独特价值。

+0

为什么将字典包装在DataFrame中是一个好主意? – Merlin1896

+0

@ Merlin1896为了向你展示整齐的输出:)你不需要这样做。 –

+0

啊,明白了! :) – Merlin1896

创建一个False值的数组。遍历groupby以获取标签的索引位置,并将其设置为True

d = {} 
empty_labels = np.array([False] * len(labels)) 
for label, group in pd.DataFrame(labels, columns=['labels']).groupby('labels'): 
    d[label] = empty_labels.copy() 
    d[label][group] = True 
>>> d 
{0: array([ True, False, False, False, False, False, False, False, False, 
     False, False, False], dtype=bool), 
1: array([False, True, False, False, False, False, False, False, False, 
     False, False, False], dtype=bool), 
2: array([False, False, True, False, False, False, False, False, False, 
     False, False, False], dtype=bool), 
3: array([False, False, False, True, False, False, False, False, False, 
     False, False, False], dtype=bool)} 

速度应该与pd.get_dummies相等。

你可以使用statsmodels.tools.tools.categorical,这应该是相当快,尤其是如果你已经有一个NumPy数组可以使用。

categorical(np.array(labels), drop=True).astype(bool) 

如果希望得到的阵列中的每列和其相应的标签之间的显式映射,传dictnames=Truecategory

演示

>>> from statsmodels.tools.tools import categorical 
>>> labels = np.array([0,0,1,1,3,3,3,1,2,1,0,0]) 
>>> categorical(labels, drop=True).astype(bool) 
array([[ True, False, False, False], 
     [ True, False, False, False], 
     [False, True, False, False], 
     [False, True, False, False], 
     [False, False, False, True], 
     [False, False, False, True], 
     [False, False, False, True], 
     [False, True, False, False], 
     [False, False, True, False], 
     [False, True, False, False], 
     [ True, False, False, False], 
     [ True, False, False, False]], dtype=bool) 

>>> res, d = categorical(np.array(labels), drop=True, dictnames=True) 
>>> d 
{0: 0, 1: 1, 2: 2, 3: 3} 

粗略的基准(假设已经NumPy的阵列)

您的数据集:

>>> %timeit categorical(labels, drop=True).astype(bool) 
14.1 µs ± 519 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

大数据集:labels = np.random.randint(0, 4, 10000)

%timeit categorical(labels, drop=True).astype(bool) 
360 µs ± 9.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+0

这似乎没有产生所需的输出格式。尤其是,如果标签数组在数字中不连续,例如。标签= [1,1,0,1,5,6,12,,12,4,5]。 – Merlin1896

+0

@ Merlin1896请问大纲在这种情况下输出格式有什么问题?对我来说,它看起来和我所预期的一样。 – miradulo

+0

With labels = np.array([0,0,2,3,2,12])a = categorical(labels,drop = True).astype(bool)的输出不会给我对原始标签的引用。 a [:,0]是标签0所需的输出,但a [:,1]是标签2的输出。 – Merlin1896