numpy.unique frozensets 有问题

numpy.unique has the problem with frozensets

只需运行代码:

a = [frozenset({1,2}),frozenset({3,4}),frozenset({1,2})]
print(set(a))  # out: {frozenset({3, 4}), frozenset({1, 2})}
print(np.unique(a)) # out: [frozenset({1, 2}), frozenset({3, 4}), frozenset({1, 2})]

第一个正确,第二个不正确。 问题就在这里:

a[0]==a[-1] # out: True

但是 np.unique 的集合有 3 个元素,而不是 2 个。

我曾经使用 np.unique 处理 ex 的重复项(使用 return_index=True 和其他)。对于这些目的,您可以建议我使用 np.unique 什么?

numpy.unique 通过排序操作,然后折叠相同元素的运行。根据文档字符串:

Returns the sorted unique elements of an array.

"sorted" 部分暗示它使用排序折叠相邻技术(类似于 *NIX sort | uniq 管道所完成的)。

问题在于,虽然 frozenset 确实定义了 __lt__([=16= 的重载],大多数 Python 排序算法将其用作其基本构建块),但它是不要将它用于总排序的目的,例如数字和序列使用它。测试 "is a proper subset of" 超载(不包括直接相等)。所以 frozenset({1,2}) < frozenset({3,4})False, 所以 frozenset({3,4}) > frozenset({1,2}).

由于预期的排序不变量被破坏,set 类对象的排序序列会产生特定于实现且基本上无用的结果。在这些情况下,基于排序的唯一化策略通常会失败;一个可能的结果是它会发现要排序的序列已经按顺序或倒序排序(因为每个元素都是 "less than" 前后元素);如果它确定它是有序的,则没有任何变化,如果它是相反的顺序,它会交换元素顺序(但在这种情况下,这与保留顺序没有区别)。然后它删除相邻的重复项(因为 post-sort,所有重复项应该分组在一起),找到 none(重复项不相邻),并 returns 原始数据。

对于 frozensets,您可能希望使用基于散列的唯一化,例如通过 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))]

第二行有点密集,所以我将其分解:

  1. sorted(a, key=sorted) - Returns 基于 a 的新 list 其中每个元素根据元素的排序 list 形式排序(因此< 比较实际上确实把喜欢和喜欢)
  2. groupby(...) returns key/group-iterator 对的迭代器。 groupby 没有 key 参数,这仅意味着每个键都是一个唯一值,并且组迭代器会生成该值的次数与所看到的次数一样多。
  3. [k for k, _ in ...] 因为我们不关心每个重复值出现了多少次,所以我们忽略了组迭代器(按照惯例分配给 _ 意味着 "ignored"),并让列表理解只产生键(唯一值)