3

只需运行代码:

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 来代替这些目的吗?

4

1 回答 1

4

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

返回数组的排序唯一元素。

“排序”部分意味着它使用了排序-折叠-相邻技术(类似于 *NIXsort | uniq管道所完成的)。

问题在于,虽然frozenset确实定义__lt__了(大多数 Python 排序算法将其用作基本构建块的 重载<),但它并没有将它用于像数字和序列那样的总排序目的使用它。测试“是一个适当的子集”(不包括直接相等)是重载的。如此frozenset({1,2}) < frozenset({3,4}),亦如此False frozenset({3,4}) > frozenset({1,2})

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

对于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)list- 根据元素的排序形式,根据a每个元素的排序位置返回一个新的list(因此<比较实际上确实将like与like放在一起)
  2. groupby(...)返回键/组迭代器对的迭代器。没有key参数groupby,它只是意味着每个键都是一个唯一的值,并且 group-iterator 产生该值的次数与看到的一样多。
  3. [k for k, _ in ...]因为我们不关心每个重复值被看到多少次,所以我们忽略了组迭代器(分配给_按惯例意味着“忽略”),并且让列表推导只产生键(唯一值)
于 2019-11-19T22:20:26.537 回答