1

我的代码需要大约两个小时来处理。瓶颈在于 for 循环和 if 语句(参见代码中的注释)。我是 python 的初学者 :) 任何人都可以推荐一种有效的 python 方法来替换嵌套的 for 和 if 语句吗?

我有大约 3000 万行的表,每行都有 (x,y,z) 值:

20.0 11.3 7
21.0 11.3 0
22.0 11.3 3
...

我想要的输出是 x、y、min(z)、count(min(z)) 形式的表格。最后一列是该 (x,y) 处的最小 z 值的最终计数。例如:

20.0 11.3 7 7
21.0 11.3 0 10
22.0 11.3 3 1
...

只有大约 600 个唯一坐标,因此输出表将为 600x4。我的代码:

import numpy as np
file = open('input.txt','r');

coordset = set()
data = np.zeros((600,4))*np.nan
irow = 0 
ctr = 0 

for row in file:
    item = row.split()
    x = float(item[0])
    y = float(item[1])
    z = float(item[2])

    # build unique grid of coords
    if ((x,y)) not in coordset:
        data[irow][0] = x 
        data[irow][1] = y 
        data[irow][2] = z 
        irow = irow + 1     # grows up to 599 

    # lookup table of unique coords
    coordset.add((x,y))

    # BOTTLENECK. replace ifs? for?
    for i in range(0, irow):
        if data[i][0]==x and data[i][1]==y:
            if z > data[i][2]:
                continue
            elif z==data[i][2]:
                ctr = ctr + 1
                data[i][3]=ctr
            if z < data[i][2]:
                data[i][2] = z
                ctr = 1
                data[i][3]=ctr

编辑:作为参考,@Joowani 的方法以 1 分 26 秒计算。我原来的方法,同一台计算机,相同的数据文件,106m23s。 编辑2 : @Ophion 和@Sibster 感谢您的建议,我没有足够的信用来 +1 有用的答案。

4

3 回答 3

2

您的解决方案似乎很慢,因为每次进行更新时它都会遍历列表(即数据) 。更好的方法是使用字典,每次更新需要 O(1) 而不是 O(n)。

这是我使用字典的解决方案:

file = open('input.txt', 'r')

#coordinates
c = {}

for line in file:
    #items
    (x, y, z) = (float(n) for n in line.split())

    if (x, y) not in c:
        c[(x, y)] = [z, 1]
    elif c[(x, y)][0] > z:
        c[(x, y)][0], c[(x, y)][1] = z, 1
    elif c[(x, y)][0] == z:
        c[(x, y)][1] += 1

for key in c:
    print("{} {} {} {}".format(key[0], key[1], c[key][0], c[key][1]))
于 2013-08-19T07:13:23.913 回答
0

为什么不将最后一个 if 更改为 elif ?

就像现在完成的那样,您将评估z < data[i][2]:循环的每次迭代。

你甚至可以用 else 替换它,因为你已经检查过了if z>data[i][2]z == data[i][2]所以唯一剩下的可能性是z < data[i][2]:

所以下面的代码会做同样的事情并且应该更快:

        if z > data[i][2]:
            continue
        elif z==data[i][2]:
            ctr = ctr + 1
            data[i][3]=ctr
        else:
            data[i][2] = z
            ctr = 1
            data[i][3]=ctr
于 2013-08-19T07:00:30.843 回答
0

要在 numpy 中使用np.unique.

def count_unique(arr):
    row_view=np.ascontiguousarray(a).view(np.dtype((np.void,a.dtype.itemsize * a.shape[1])))
    ua, uind = np.unique(row_view,return_inverse=True)
    unique_rows = ua.view(a.dtype).reshape(ua.shape + (-1,))
    count=np.bincount(uind)
    return np.hstack((unique_rows,count[:,None]))

首先让我们检查一个小数组:

a=np.random.rand(10,3)
a=np.around(a,0)

print a
[[ 0.  0.  0.]
 [ 0.  1.  1.]
 [ 0.  1.  0.]
 [ 1.  0.  0.]
 [ 0.  1.  1.]
 [ 1.  1.  0.]
 [ 1.  0.  1.]
 [ 1.  0.  1.]
 [ 1.  0.  0.]
 [ 0.  0.  0.]]

 print output
[[ 0.  0.  0.  2.]
 [ 0.  1.  0.  1.]
 [ 0.  1.  1.  2.]
 [ 1.  0.  0.  2.]
 [ 1.  0.  1.  2.]
 [ 1.  1.  0.  1.]]

 print np.sum(output[:,-1])
 10

看起来挺好的!现在让我们检查一个大数组:

a=np.random.rand(3E7,3)
a=np.around(a,1)

output=count_unique(a)
print output.shape
(1331, 4)  #Close as I can get to 600 unique elements.

print np.sum(output[:,-1])
30000000.0

在我的机器和 3GB 内存上大约需要 33 秒,在大型阵列的内存中完成这一切可能会成为您的瓶颈。作为参考,@Joowani 的解决方案大约需要 130 秒,尽管这有点像苹果和橘子的比较,因为我们从一个 numpy 数组开始。您的里程可能会有所不同。

要将数据作为 numpy 数组读取,我会在此处查看问题,但它应该类似于以下内容:

arr=np.genfromtxt("./input.txt", delimiter=" ")

从 txt 文件中加载这么多数据,我真的建议使用该pandas链接中的示例。

于 2013-08-19T13:11:50.790 回答