Numpy.unique 的行为(扁平化不一致性?)

2 投票
1 回答
760 浏览
提问于 2025-04-20 01:47

我有两个列表,想要找到与唯一配对相关的索引(我在网上找到的帖子大多只关注配对本身)。我尝试使用numpy.unique来实现这个目标,但遇到了一些奇怪的问题。我把这两个列表合并成一个元组列表,然后用set()np.unique()成功地找到了唯一的配对,但我想要的是原始列表中的索引。unique的文档说明,如果设置return_inverse=True,它会返回这些索引。然而,我发现如果设置与不设置这个选项,输出的“扁平化”程度是不同的。

在这个例子中,我使用字符串只是为了避免比较问题,实际上它们是浮点数。

import numpy as np

l_1 = ['12.34', '12.34', '12.34', '12.34', '56.78', '56.78', '90.12', '90.12']
l_2 = ['-1.23', '-1.23', '-4.56', '-4.56', '-6.78', '-6.78', '-9.01', '-9.01']
ll = zip(l_1, l_2)

ull1 = np.unique(ll)

ull2, inds = np.unique(ll, return_inverse=True)

在第一种情况下,配对作为输出的第二个维度被保留。而在第二种情况下,甚至连元组都被扁平化了,这样就破坏了配对的结构。

In [1]: ull1
Out[1]: 
array([['-9.01', '90.12'],
       ['-1.23', '12.34'],
       ['-6.78', '56.78'],
       ['-4.56', '12.34']], 
      dtype='|S5')

In [2]: ull2
Out[2]:
array(['-1.23', '-4.56', '-6.78', '-9.01', '12.34', '56.78', '90.12'], 
      dtype='|S5')

这是故意这样设计的吗?有没有办法让unique在第一种情况下给我想要的索引(类似于[[6,7], [0,1], [4,5], [2,3]])?我从文档中看不出哪种行为是异常的。

我需要这些索引来处理其他类似列表中的值。如果我能使用pandas就好了,但我现在的电脑上只有一个非常旧版本的numpy,没有pandas。不过,即使在numpy 1.8.1中,这个问题依然存在。我知道我可以做类似下面的事情:

sll = list(set(ll))
for i in range(len(sll)):
    inds = np.where([val == sll[i] for val in ll])
    # I do my operations here using inds

但我希望能有更优雅的解决方案。

1 个回答

4

在版本1.8.1中,numpy.unique的源代码开头是这样的:

try:
    ar = ar.flatten()
except AttributeError:
    if not return_inverse and not return_index:
        return np.sort(list(set(ar)))
    else:
        ar = np.asanyarray(ar).flatten()

如果输入的不是一个数组,并且没有提供return_inversereturn_index这两个选项,那么这个程序就会使用Python自带的功能来找出唯一的元素。不过,它的做法有点问题;它没有按照文档所保证的那样进行“扁平化”处理:

输入数组。如果它不是一维的,将会被扁平化。

正如Jaime在评论中提到的,这个问题在当前的NumPy主分支中已经修复了。


我认为你可以通过将你的两个列表打包成一个结构化数组来获得你想要的结果。我不确定numpy.unique是否支持结构化数组,但如果不支持,你可以使用numpy.sort来模拟它的功能,文档中有说明如何与结构化数组一起使用。

撰写回答