从Numpy矩阵构造Python集

时间:2009-12-21 10:02:03

标签: python arrays numpy set

我正在尝试执行以下

>> from numpy import *
>> x = array([[3,2,3],[4,4,4]])
>> y = set(x)
TypeError: unhashable type: 'numpy.ndarray'

如何使用Numpy数组中的所有元素轻松高效地创建集合?

6 个答案:

答案 0 :(得分:26)

如果你想要一组元素,这是另一种可能更快的方法:

y = set(x.flatten())

PS:在10x100阵列上对x.flatx.flatten()x.ravel()进行比较后,我发现它们都以大致相同的速度运行。对于3x3阵列,最快的版本是迭代器版本:

y = set(x.flat)

我会推荐它,因为它是内存较少的版本(它可以很好地扩展到数组的大小)。

PS :还有一个类似的NumPy函数:

y = numpy.unique(x)

这确实产生了一个NumPy数组,其元素与set(x.flat)相同,但是作为NumPy数组。这非常快(几乎快10倍),但是如果你需要set,那么执行set(numpy.unique(x))比其他程序慢一点(构建一个集合会带来很大的开销)。

答案 1 :(得分:14)

数组的不可变对应元素是元组,因此,尝试将数组数组转换为元组数组:

>> from numpy import *
>> x = array([[3,2,3],[4,4,4]])

>> x_hashable = map(tuple, x)

>> y = set(x_hashable)
set([(3, 2, 3), (4, 4, 4)])

答案 2 :(得分:7)

如果您想要创建ndarray中包含的元素的集合,但是如果您想创建一组ndarray个对象,则上述答案有效 - 或者使用ndarray个对象作为字典中的键 - 然后你必须为它们提供一个可混合的包装器。有关简单示例,请参阅下面的代码:

from hashlib import sha1

from numpy import all, array, uint8


class hashable(object):
    r'''Hashable wrapper for ndarray objects.

        Instances of ndarray are not hashable, meaning they cannot be added to
        sets, nor used as keys in dictionaries. This is by design - ndarray
        objects are mutable, and therefore cannot reliably implement the
        __hash__() method.

        The hashable class allows a way around this limitation. It implements
        the required methods for hashable objects in terms of an encapsulated
        ndarray object. This can be either a copied instance (which is safer)
        or the original object (which requires the user to be careful enough
        not to modify it).
    '''
    def __init__(self, wrapped, tight=False):
        r'''Creates a new hashable object encapsulating an ndarray.

            wrapped
                The wrapped ndarray.

            tight
                Optional. If True, a copy of the input ndaray is created.
                Defaults to False.
        '''
        self.__tight = tight
        self.__wrapped = array(wrapped) if tight else wrapped
        self.__hash = int(sha1(wrapped.view(uint8)).hexdigest(), 16)

    def __eq__(self, other):
        return all(self.__wrapped == other.__wrapped)

    def __hash__(self):
        return self.__hash

    def unwrap(self):
        r'''Returns the encapsulated ndarray.

            If the wrapper is "tight", a copy of the encapsulated ndarray is
            returned. Otherwise, the encapsulated ndarray itself is returned.
        '''
        if self.__tight:
            return array(self.__wrapped)

        return self.__wrapped

使用包装器类很简单:

>>> from numpy import arange

>>> a = arange(0, 1024)
>>> d = {}
>>> d[a] = 'foo'
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray'
>>> b = hashable(a)
>>> d[b] = 'bar'
>>> d[b]
'bar'

答案 3 :(得分:3)

如果你想要一组元素:

>> y = set(e for r in x
             for e in r)
set([2, 3, 4])

对于一组行:

>> y = set(tuple(r) for r in x)
set([(3, 2, 3), (4, 4, 4)])

答案 4 :(得分:0)

我喜欢xperroni's idea。但我认为使用ndarray的直接继承而不是包装它可以简化实现。

from hashlib import sha1
from numpy import ndarray, uint8, array

class HashableNdarray(ndarray):
    def __hash__(self):
        if not hasattr(hasattr, '__hash'):
            self.__hash = int(sha1(self.view(uint8)).hexdigest(), 16)
        return self.__hash

    def __eq__(self, other):
        if not isinstance(other, HashableNdarray):
            return super(HashableNdarray, self).__eq__(other)
        return super(HashableNdarray, self).__eq__(super(HashableNdarray, other)).all()

NumPy ndarray可以被视为派生类并用作可清除对象。 view(ndarray)可用于反向转换,但在大多数情况下甚至不需要它。

>>> a = array([1,2,3])
>>> b = array([2,3,4])
>>> c = array([1,2,3])
>>> s = set()

>>> s.add(a.view(HashableNdarray))
>>> s.add(b.view(HashableNdarray))
>>> s.add(c.view(HashableNdarray))
>>> print(s)
{HashableNdarray([2, 3, 4]), HashableNdarray([1, 2, 3])}
>>> d = next(iter(s))
>>> print(d == a)
[False False False]
>>> import ctypes
>>> print(d.ctypes.data_as(ctypes.POINTER(ctypes.c_double)))
<__main__.LP_c_double object at 0x7f99f4dbe488>

答案 5 :(得分:0)

添加到@Eric Lebigot和他的精彩文章中。

以下是构建张量查找表的技巧:

a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
np.unique(a, axis=0)

输出:

array([[1, 0, 0], [2, 3, 4]])

np.unique documentation