更有效的交叉路口计算方法?

时间:2009-12-15 22:07:01

标签: python algorithm set

我有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. 与每个蒙版 - 蒙版对相交的轨道数(以创建连接矩阵)
  2. 与每个蒙版相交的轨道子集,以便为子集中的每个轨道的每个(x,y,z)坐标添加1(以创建“密度”图像)
  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个或更多掩码的列表时,这两个部分仍然需要一个多小时。有没有比为每个轨道迭代更有效的方法呢?

6 个答案:

答案 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 matrixm * 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,更小的乘数):

  1. 不要在每个曲目和面具上多次调用set:每个曲目调用一次,每个掩码调用一次,以设置辅助“并行”集合列表,然后处理这些
  2. if any(someset):在语义上与if someset:相同,但速度稍慢
  3. 不会产生显着的影响,但可能会有所帮助。

答案 5 :(得分:0)

Lame建议我可以做出另一项可能的改进,但是:

使用Python的长整数可以将小整数集建模为位向量。假设您用一个小的整数id替换每个元组,然后将每个轨道和每组mask-coords转换为一组这些小ID。你可以将这些集合表示为长整数,使交集操作更快(但不是渐近更快)。