numpy.unique
通过排序操作,然后折叠相同元素的运行。根据文档字符串:
返回数组的排序唯一元素。
“排序”部分意味着它使用了排序-折叠-相邻技术(类似于 *NIXsort | uniq
管道所完成的)。
问题在于,虽然frozenset
确实定义__lt__
了(大多数 Python 排序算法将其用作基本构建块的 重载<
),但它并没有将它用于像数字和序列那样的总排序目的使用它。测试“是一个适当的子集”(不包括直接相等)是重载的。如此frozenset({1,2}) < frozenset({3,4})
,亦如此False
。 frozenset({3,4}) > frozenset({1,2})
由于预期的排序不变量被破坏,类对象的排序序列set
会产生特定于实现且基本上无用的结果。在这些条件下,基于排序的唯一化策略通常会失败;一个可能的结果是它会找到已经按顺序或倒序排序的序列(因为每个元素都“小于”前面和后面的元素);如果它确定它是有序的,则没有任何变化,如果它是相反的顺序,它会交换元素顺序(但在这种情况下,这与保留顺序没有区别)。然后它删除相邻的重复项(因为后排序,所有重复项都应该组合在一起),找到一个(重复项不相邻),并返回原始数据。
对于frozenset
s,您可能希望使用基于哈希的唯一性,例如通过set
或(以保留 Python 3.7+ 上的原始出现顺序)dict.fromkeys
,;后者很简单:
a = [frozenset({1,2}),frozenset({3,4}),frozenset({1,2})]
uniqa = list(dict.fromkeys(a)) # Works on CPython/PyPy 3.6 as implementation detail, and on 3.7+ everywhere
也可以使用基于排序的唯一性,但numpy.unique
似乎不支持key
函数,因此更容易坚持使用 Python 内置工具:
from itertools import groupby # With no key argument, can be used much like uniq command line tool
a = [frozenset({1,2}),frozenset({3,4}),frozenset({1,2})]
uniqa = [k for k, _ in groupby(sorted(a, key=sorted))]
第二行有点密集,所以我将其分解:
sorted(a, key=sorted)
list
- 根据元素的排序形式,根据a
每个元素的排序位置返回一个新的list
(因此<
比较实际上确实将like与like放在一起)
groupby(...)
返回键/组迭代器对的迭代器。没有key
参数groupby
,它只是意味着每个键都是一个唯一的值,并且 group-iterator 产生该值的次数与看到的一样多。
[k for k, _ in ...]
因为我们不关心每个重复值被看到多少次,所以我们忽略了组迭代器(分配给_
按惯例意味着“忽略”),并且让列表推导只产生键(唯一值)