我有300000个列表(光纤轨道)的列表,其中每个轨道是(x,y,z)元组/坐标的列表:
tracks=
[[(1,2,3),(3,2,4),...]
[(4,2,1),(5,7,3),...]
...
]
我还有一组蒙版,每个蒙版定义为(x,y,z)元组/坐标列表:
mask_coords_list=
[[(1,2,3),(8,13,4),...]
[(6,2,2),(5,7,3),...]
...
]
我试图找到所有可能的面具对:
我目前正在做第1部分:
def mask_connectivity_matrix(tracks,masks,masks_coords_list):
connect_mat=zeros((len(masks),len(masks)))
for track in tracks:
cur=[]
for count,mask_coords in enumerate(masks_coords_list):
if any(set(track) & set(mask_coords)):
cur.append(count)
for x,y in list(itertools.combinations(cur,2)):
connect_mat[x,y] += 1
和第2部分如此:
def mask_tracks(tracks,masks,masks_coords_list):
vox_tracks_img=zeros((xdim,ydim,zdim,len(masks)))
for track in tracks:
for count,mask in enumerate(masks_coords_list):
if any(set(track) & set(mask)):
for x,y,z in track:
vox_tracks_img[x,y,z,count] += 1
使用集合来查找交叉点已大大加快了这一过程,但是当我有70个或更多掩码的列表时,这两个部分仍然需要一个多小时。有没有比为每个轨道迭代更有效的方法呢?
答案 0 :(得分:3)
线性化体素坐标,并将它们放入两个scipy.sparse.sparse.csc矩阵中。
设v是体素的数量,m是掩模的数量,t是轨道的数量
令M为掩模csc矩阵,大小(m x v),其中1 at(i,j)表示掩模i与体素j重叠。
令T为轨道csc矩阵,大小(t×v),其中a(k,j)表示轨道k与体素j重叠。
Overlap = (M * T.transpose() > 0) # track T overlaps mask M
Connected = (Overlap * Overlap.tranpose() > 0) # Connected masks
Density[mask_idx] = numpy.take(T, nonzero(Overlap[mask_idx, :])[0], axis=0).sum(axis=0)
我可能在最后一个错了,我不确定css_matrices可以通过非零和&采取。您可能需要在循环中拉出每列并将其转换为完整矩阵。
我尝试了一些试验来模拟我认为合理数量的数据。在2年前的MacBook上,下面的代码大约需要2分钟。如果使用csr_matrices,则大约需要4分钟。根据每首曲目的长度,可能需要权衡。
from numpy import *
from scipy.sparse import csc_matrix
nvox = 1000000
ntracks = 300000
nmask = 100
# create about 100 entries per track
tcoords = random.uniform(0, ntracks, ntracks * 100).astype(int)
vcoords = random.uniform(0, nvox, ntracks * 100).astype(int)
d = ones(ntracks * 100)
T = csc_matrix((d, vstack((tcoords, vcoords))), shape=(ntracks, nvox), dtype=bool)
# create around 10000 entries per mask
mcoords = random.uniform(0, nmask, nmask * 10000).astype(int)
vcoords = random.uniform(0, nvox, nmask * 10000).astype(int)
d = ones(nmask * 10000)
M = csc_matrix((d, vstack((mcoords, vcoords))), shape=(nmask, nvox), dtype=bool)
Overlap = (M * T.transpose()).astype(bool) # mask M overlaps track T
Connected = (Overlap * Overlap.transpose()).astype(bool) # mask M1 and M2 are connected
Density = Overlap * T.astype(float) # number of tracks overlapping mask M summed across voxels
答案 1 :(得分:1)
首先,你需要知道哪些曲目与哪些面具重合,incidence matrix。
import numpy
from collections import defaultdict
def by_point(sets):
d = defaultdict(list)
for i, s in enumerate(sets):
for pt in s:
d[pt].append(i)
return d
def calc(xdim, ydim, zdim, mask_coords_list, tracks):
masks_by_point = by_point(mask_coords_list)
tracks_by_point = by_point(tracks)
a = numpy.zeros((len(mask_coords_list), len(tracks)), dtype=int)
for pt, maskids in masks_by_point.iteritems():
for trackid in tracks_by_point.get(pt, ()):
a[maskids, trackid] = 1
m = numpy.matrix(a)
您正在寻找的adjacency matrix是m * m.T
。
到目前为止,您所拥有的代码仅计算上三角形。您可以使用triu
抓住这一半。
am = m * m.T # calculate adjacency matrix
am = numpy.triu(am, 1) # keep only upper triangle
am = am.A # convert matrix back to array
体素计算也可以使用关联矩阵。
vox_tracks_img = numpy.zeros((xdim, ydim, zdim, len(mask_coords_list)), dtype=int)
for trackid, track in enumerate(tracks):
for x, y, z in track:
vox_tracks_img[x, y, z, :] += a[:,trackid]
return am, vox_tracks_img
对于我而言,对于拥有数百个蒙版和曲目的数据集,它会在一秒钟内运行。
如果您有多个点出现在蒙版中但不在任何轨道上,则在进入循环之前从masks_by_point
删除这些点的条目可能是值得的。
答案 2 :(得分:0)
您可以首先将两个函数组合在一起创建两个结果。此外,在循环之前不需要列出组合,因为它已经是一个生成器,这可能会节省你一些时间。
def mask_connectivity_matrix_and_tracks(tracks,masks,masks_coords_list):
connect_mat=zeros((len(masks),len(masks)))
vox_tracks_img=zeros((xdim,ydim,zdim,len(masks)))
for track in tracks:
cur=[]
for count,mask_coords in enumerate(masks_coords_list):
if any(set(track) & set(mask_coords)):
cur.append(count)
for x,y,z in track:
vox_tracks_img[x,y,z,count] += 1
for x,y in itertools.combinations(cur,2):
connect_mat[x,y] += 1
此外,这可能永远不会像“在我们死之前完成”那样“快速”,所以最好的方法是最终用Cython编译它作为python的c模块。
答案 3 :(得分:0)
如果存储了每个掩码点集:
(1,2,3),(1,2,4),(1,3,1)作为这样的字典:{1: [{2: set([3, 4])}, {3: set([1])}]}
,你可能最终能够更快地检查匹配......但是也许不吧。
答案 4 :(得分:0)
通过删除冗余操作,可以获得次要优化(相同的大O,更小的乘数):
set
:每个曲目调用一次,每个掩码调用一次,以设置辅助“并行”集合列表,然后处理这些if any(someset):
在语义上与if someset:
相同,但速度稍慢不会产生显着的影响,但可能会有所帮助。
答案 5 :(得分:0)
Lame建议我可以做出另一项可能的改进,但是:
使用Python的长整数可以将小整数集建模为位向量。假设您用一个小的整数id替换每个元组,然后将每个轨道和每组mask-coords转换为一组这些小ID。你可以将这些集合表示为长整数,使交集操作更快(但不是渐近更快)。