在scipy中,我们可以使用scipy.sparse.lil_matrix()等构造一个稀疏矩阵。但是该矩阵是二维的。
我想知道Python中是否存在稀疏3d矩阵/数组(张量)的数据结构?
ps我在3d中有很多稀疏数据,需要张量来存储/执行乘法。如果没有现有的数据结构,是否有实现此类张量的建议?
在scipy中,我们可以使用scipy.sparse.lil_matrix()等构造一个稀疏矩阵。但是该矩阵是二维的。
我想知道Python中是否存在稀疏3d矩阵/数组(张量)的数据结构?
ps我在3d中有很多稀疏数据,需要张量来存储/执行乘法。如果没有现有的数据结构,是否有实现此类张量的建议?
Answers:
很高兴为您建议一个(可能很明显)的实现,如果您有时间和空间来建立新的依赖关系,并且需要更快的速度,可以用纯Python或C / Cython进行。
N维中的稀疏矩阵可以假定大多数元素为空,因此我们使用以元组为键的字典:
class NDSparseMatrix:
def __init__(self):
self.elements = {}
def addValue(self, tuple, value):
self.elements[tuple] = value
def readValue(self, tuple):
try:
value = self.elements[tuple]
except KeyError:
# could also be 0.0 if using floats...
value = 0
return value
并且您会这样使用它:
sparse = NDSparseMatrix()
sparse.addValue((1,2,3), 15.7)
should_be_zero = sparse.readValue((1,5,13))
您可以通过验证输入实际上是一个元组并且仅包含整数来使此实现更健壮,但是这样做只会减慢速度,因此除非您稍后将代码发布给外界,否则我不会担心。
编辑-假设其他张量是N维NumPy数组(numpy.ndarray
),则矩阵乘法问题的Cython实现如下所示:
#cython: boundscheck=False
#cython: wraparound=False
cimport numpy as np
def sparse_mult(object sparse, np.ndarray[double, ndim=3] u):
cdef unsigned int i, j, k
out = np.ndarray(shape=(u.shape[0],u.shape[1],u.shape[2]), dtype=double)
for i in xrange(1,u.shape[0]-1):
for j in xrange(1, u.shape[1]-1):
for k in xrange(1, u.shape[2]-1):
# note, here you must define your own rank-3 multiplication rule, which
# is, in general, nontrivial, especially if LxMxN tensor...
# loop over a dummy variable (or two) and perform some summation:
out[i,j,k] = u[i,j,k] * sparse((i,j,k))
return out
尽管您总是需要为遇到的问题手动解决这个问题,但由于(如代码注释中所述),您将需要定义要累加的索引,并注意数组长度或不起作用!
编辑2-如果另一个矩阵也是稀疏的,那么您就不需要进行三种方式的循环:
def sparse_mult(sparse, other_sparse):
out = NDSparseMatrix()
for key, value in sparse.elements.items():
i, j, k = key
# note, here you must define your own rank-3 multiplication rule, which
# is, in general, nontrivial, especially if LxMxN tensor...
# loop over a dummy variable (or two) and perform some summation
# (example indices shown):
out.addValue(key) = out.readValue(key) +
other_sparse.readValue((i,j,k+1)) * sparse((i-3,j,k))
return out
我对C实现的建议是使用一个简单的结构来保存索引和值:
typedef struct {
int index[3];
float value;
} entry_t;
然后,您将需要一些函数来分配和维护此类结构的动态数组,并根据需要快速搜索它们;但是在担心这些问题之前,您应该先测试Python实现的性能。
scipy.sparse.dok_matrix
。这就是您在此处描述的内容,仅限于2D。扩展它以容纳ND数据很容易,但是您如何处理这些数据呢?(话虽这么说,您的回答是完全合理的……)
u
在这种情况下),确定吗?除非两者都很稀疏,否则在这种情况下我会被误解。在这种情况下,您只需在字典中的键上循环,然后从元组中提取索引。无论如何,我不能跟上稀疏代数的发展,更不用说优化该主题算法的计算机科学了。抱歉,@ zhongqi!
看看sparray-Python中的稀疏n维数组(作者Jan Erik Solem)。也可以在github上找到。
截至2017年的另一种答案是sparse
一揽子计划。根据程序包本身,它在NumPy之上并scipy.sparse
通过概括scipy.sparse.coo_matrix
布局来实现稀疏多维数组。
这是从文档中获取的示例:
import numpy as np
n = 1000
ndims = 4
nnz = 1000000
coords = np.random.randint(0, n - 1, size=(ndims, nnz))
data = np.random.random(nnz)
import sparse
x = sparse.COO(coords, data, shape=((n,) * ndims))
x
# <COO: shape=(1000, 1000, 1000, 1000), dtype=float64, nnz=1000000>
x.nbytes
# 16000000
y = sparse.tensordot(x, x, axes=((3, 0), (1, 2)))
y
# <COO: shape=(1000, 1000, 1000, 1000), dtype=float64, nnz=1001588>
最好使用scipy的稀疏模块,而不是从头开始编写所有新内容。这可能会导致(更好)的性能。我有一个类似的问题,但是我只需要有效地访问数据,而不需要对它们执行任何操作。此外,我的数据在三个维度中只有两个是稀疏的。
我编写了解决我的问题的课程,并且可以轻松扩展(以满足我的想法)以满足OP的需求。不过,它可能仍具有改进的潜力。
import scipy.sparse as sp
import numpy as np
class Sparse3D():
"""
Class to store and access 3 dimensional sparse matrices efficiently
"""
def __init__(self, *sparseMatrices):
"""
Constructor
Takes a stack of sparse 2D matrices with the same dimensions
"""
self.data = sp.vstack(sparseMatrices, "dok")
self.shape = (len(sparseMatrices), *sparseMatrices[0].shape)
self._dim1_jump = np.arange(0, self.shape[1]*self.shape[0], self.shape[1])
self._dim1 = np.arange(self.shape[0])
self._dim2 = np.arange(self.shape[1])
def __getitem__(self, pos):
if not type(pos) == tuple:
if not hasattr(pos, "__iter__") and not type(pos) == slice:
return self.data[self._dim1_jump[pos] + self._dim2]
else:
return Sparse3D(*(self[self._dim1[i]] for i in self._dim1[pos]))
elif len(pos) > 3:
raise IndexError("too many indices for array")
else:
if (not hasattr(pos[0], "__iter__") and not type(pos[0]) == slice or
not hasattr(pos[1], "__iter__") and not type(pos[1]) == slice):
if len(pos) == 2:
result = self.data[self._dim1_jump[pos[0]] + self._dim2[pos[1]]]
else:
result = self.data[self._dim1_jump[pos[0]] + self._dim2[pos[1]], pos[2]].T
if hasattr(pos[2], "__iter__") or type(pos[2]) == slice:
result = result.T
return result
else:
if len(pos) == 2:
return Sparse3D(*(self[i, self._dim2[pos[1]]] for i in self._dim1[pos[0]]))
else:
if not hasattr(pos[2], "__iter__") and not type(pos[2]) == slice:
return sp.vstack([self[self._dim1[pos[0]], i, pos[2]]
for i in self._dim2[pos[1]]]).T
else:
return Sparse3D(*(self[i, self._dim2[pos[1]], pos[2]]
for i in self._dim1[pos[0]]))
def toarray(self):
return np.array([self[i].toarray() for i in range(self.shape[0])])
scipy
稀疏数组模块中。你考虑过了吗?
我还需要3D稀疏矩阵来求解2D热方程(2个空间维是密集的,但时间维是对角线加上正负1个对角线。)我发现此链接可以为我提供指导。诀窍是创建一个Number
将2D稀疏矩阵映射到1D线性向量的数组。然后通过构建数据和索引列表来构建2D矩阵。后来,Number
矩阵用于将答案排列回2D数组。
[编辑]它发生在我后,我的初始后,这可以处理通过使用更好的.reshape(-1)
方法。经过研究,该reshape
方法比flatten
因为将新视图返回到原始数组中但flatten
复制该数组要好。该代码使用原始Number
数组。我稍后会尝试更新。[结束编辑]
我通过创建一维随机矢量并求解第二个矢量来对其进行测试。然后将其乘以稀疏2D矩阵,得到相同的结果。
注意:我在一个循环中使用完全相同的矩阵M重复了很多次,因此您可能会认为求解inverse(
M会更有效)
。但逆中号是不是稀疏的,所以我想用(但没有测试)spsolve
是一个更好的解决方案。“最佳”可能取决于您使用的矩阵大小。
#!/usr/bin/env python3
# testSparse.py
# profhuster
import numpy as np
import scipy.sparse as sM
import scipy.sparse.linalg as spLA
from array import array
from numpy.random import rand, seed
seed(101520)
nX = 4
nY = 3
r = 0.1
def loadSpNodes(nX, nY, r):
# Matrix to map 2D array of nodes to 1D array
Number = np.zeros((nY, nX), dtype=int)
# Map each element of the 2D array to a 1D array
iM = 0
for i in range(nX):
for j in range(nY):
Number[j, i] = iM
iM += 1
print(f"Number = \n{Number}")
# Now create a sparse matrix of the "stencil"
diagVal = 1 + 4 * r
offVal = -r
d_list = array('f')
i_list = array('i')
j_list = array('i')
# Loop over the 2D nodes matrix
for i in range(nX):
for j in range(nY):
# Recall the 1D number
iSparse = Number[j, i]
# populate the diagonal
d_list.append(diagVal)
i_list.append(iSparse)
j_list.append(iSparse)
# Now, for each rectangular neighbor, add the
# off-diagonal entries
# Use a try-except, so boundry nodes work
for (jj,ii) in ((j+1,i),(j-1,i),(j,i+1),(j,i-1)):
try:
iNeigh = Number[jj, ii]
if jj >= 0 and ii >=0:
d_list.append(offVal)
i_list.append(iSparse)
j_list.append(iNeigh)
except IndexError:
pass
spNodes = sM.coo_matrix((d_list, (i_list, j_list)), shape=(nX*nY,nX*nY))
return spNodes
MySpNodes = loadSpNodes(nX, nY, r)
print(f"Sparse Nodes = \n{MySpNodes.toarray()}")
b = rand(nX*nY)
print(f"b=\n{b}")
x = spLA.spsolve(MySpNodes.tocsr(), b)
print(f"x=\n{x}")
print(f"Multiply back together=\n{x * MySpNodes}")
我需要一个三维的查找表中的x,y,z和与此解决方案..想出了
为什么不使用其中一个维度是第三个维度的除数?即。使用x和yz作为矩阵尺寸
from scipy import sparse
m = sparse.lil_matrix((100,2000), dtype=float)
def add_element((x,y,z), element):
element=float(element)
m[x,y+z*100]=element
def get_element(x,y,z):
return m[x,y+z*100]
add_element([3,2,4],2.2)
add_element([20,15,7], 1.2)
print get_element(0,0,0)
print get_element(3,2,4)
print get_element(20,15,7)
print " This is m sparse:";print m
====================
OUTPUT:
0.0
2.2
1.2
This is m sparse:
(3, 402L) 2.2
(20, 715L) 1.2
====================