我有一段简单的代码没有按预期运行。
from numpy import *
from numpy.linalg import *
from sets import Set
W = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
E = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
matrices = Set([])
matrices.add(W)
matrices.add(E)
matrices
矩阵是相同的,但是当我打印集合的内容时它们都是单独出现的。但是,如果我像下面那样分配它,那么副本就不会出现。
W = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
E = W
知道发生了什么事吗?我需要一种避免在我编写的程序中重复矩阵的方法,这会生成一吨矩阵。
编辑:我想要以下输出
set([matrix([[ 1, 1, 1, 1],
[ 1, 1, -1, -1],
[ 1, -1, 2, -2],
[ 1, -1, -2, 2]])])
但请改为:
set([matrix([[ 1, 1, 1, 1],
[ 1, 1, -1, -1],
[ 1, -1, 2, -2],
[ 1, -1, -2, 2]]), matrix([[ 1, 1, 1, 1],
[ 1, 1, -1, -1],
[ 1, -1, 2, -2],
[ 1, -1, -2, 2]])])
答案 0 :(得分:3)
这是因为集合使用 __ eq __ 和 __ 哈希 __ 特殊方法来检测项目的相等性(见http://docs.python.org/2/library/sets.html)。但矩阵对象具有不同的哈希值,那些 __ eq __ 方法不会返回true / false,而是返回矩阵:
>>> W == E
matrix([[ True, True, True, True],
[ True, True, True, True],
[ True, True, True, True],
[ True, True, True, True]], dtype=bool)
>>> W > E
matrix([[False, False, False, False],
[False, False, False, False],
[False, False, False, False],
[False, False, False, False]], dtype=bool)
答案 1 :(得分:3)
你遇到的问题是python如何在内部检查对象之间的相似性。具体而言,如何比较被认为是“可混合”的对象。
python set
构造函数决定两个对象是否相同的方式是基于调用名为__hash__
的魔术方法(以及另一个名为__eq__
的魔法)。如果在它们上调用__hash__
的结果返回相同的值(并且它们上的caling __eq__
返回True
),则认为两个对象是相同的。如果在两个对象上调用__hash__
给出不同的值,set
假定它们不能被视为相同。
值得注意的是,只有 的集合包含被认为是“可散列”的对象,即实现__hash__
方法的那些对象。
让我们看看它是如何工作的:
In [73]: a = "one"
In [74]: b = "one"
In [75]: c = "two"
In [76]: a.__hash__()
Out[76]: -261223665
In [77]: b.__hash__()
Out[77]: -261223665
In [78]: c.__hash__()
Out[78]: 323309869
In [79]: set([a,b,c])
Out[79]: set(['two', 'one'])
现在,让我们导入numpy,看看你的矩阵的哈希值是什么。
In [81]: import numpy as np
In [82]: W = np.matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
In [83]: E = np.matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
In [84]: W.__hash__()
Out[84]: 4879307
In [85]: E.__hash__()
Out[85]: 4879135
请注意,E
和W
的哈希值不同,即使它们似乎包含相同的内容。由于它们的哈希值不同,它们将在集合中显示为不同的对象。当您执行W = E
之类的作业时,名称W
和E
实际上是指同一个对象。
如果您需要解决方法,可以存储用于构建矩阵的字符串:
In [86]: set(['1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2',
'1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2'])
Out[86]: set(['1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2'])
答案 2 :(得分:2)
matrix
在__eq__
中使用它们时,没有表现良好的__hash__
和set
方法。如果要使用set
使它们唯一,则需要将矩阵包装在辅助类中。像这样简单的东西应该做;
import hashlib
class MatrixWrap:
def __init__(self, matrix):
self.matrix = matrix
def __hash__(self):
return int(hashlib.sha1(self.matrix).hexdigest(), 16)
def __eq__(self, x):
return self.__hash__() == x.__hash__()
然后就可以了;
from numpy import *
from numpy.linalg import *
W = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
E = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
X = matrix('2, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
matrices = set()
matrices.add(MatrixWrap(W))
matrices.add(MatrixWrap(E))
matrices.add(MatrixWrap(X))
for a in matrices:
print a.matrix
...列出您的独特矩阵。
答案 3 :(得分:0)
所有答案和评论都很好,并确定了问题,而@Joachim Isaksson确定了一个很好的解决方案。我想指出你也可以序列化常规数组并将数据转储/加载到集合中,如下所示:
import numpy as np
def arrayToTuple(arr):
arrType = arr.dtype.str
arrShape = arr.shape
arrData = arr.tostring()
return (arrType,arrShape,arrData)
def tupleToArray(tupl):
arrType, arrShape, arrData = tupl
return np.matrix( np.fromstring(arrData, dtype=arrType).reshape(arrShape) )
# remove the matrix( ) wrap to return arrays instead of matrices
然后你的代码看起来像这样:
W = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
E = matrix('1, 1, 1, 1; 1, 1, -1, -1; 1, -1, 2, -2; 1, -1, -2, 2')
matrixTuples = set()
matrixTuples.add(arrayToTuple(W))
matrixTuples.add(arrayToTuple(E))
for mTupl in matrixTuples:
print tupleToArray(mTupl)
这也适用于常规的bool,integer和float数组(但不是对象或字符串数组) - 只需删除arrayFromTuple返回的matrix()包装器。我想这些函数可能更好地命名为matrixToTuple和tupleToMatrix,但无论你是使用矩阵还是数组,它们都非常接近。