提高python代码的性能,旨在使用大型数据集在特定时间间隔内查找数字

时间:2016-01-14 14:52:38

标签: python performance

我有一个字典如下(为简单起见,只给出了一个键):

intervals={'Sca1': [[1428, 1876, 0.0126525], [1876, 1883, 0.0126525], [1883, 1939, 0.0126525], [1939, 1956, 0.0126525], [1956, 2032, 0.0126525], [2154, 3067, 0.0126525], [3067, 3438, 0.0126525], [3438, 3575, 0.0126525], [4301, 4610, 0.0126525], [4610, 4694, 0.0126525], [4694, 5163, 0.0126525], [5163, 5164, 0.0126525], [5164, 5530, 0.013], [5530, 5858, 0.0127005]]}

和列表如下:

snplist = [1786, 2463, 2907, 3068, 3086, 3398, 5468, 5531, 5564, 5580]

我想检查snplist中的每个值,它是否在字典值的子列表的前两个值之间的间隔中。例如,1786介于1428的{​​{1}}和1878之间。如果是,则打印该子列表的索引,在本例中为[1428, 1876, 0.0126525],来自snplist的元素,在本例中为0,子列表中的第三个值,在本例中为{{ 1}}。我写了以下代码:

1786

输出是:

0.0126525

此代码适用于这个小型数据集,但当我将其用于非常大的数据集时,它变得极其缓慢。我使用列表理解如下:

output=[]
for element in snplist:
    for key, value in intervals.items():
        for left, right, rho in value:
            if left <= element <= right:
                output.append([value.index([left, right, rho]), element, rho])
print 'output', output, '\n'

但这并没有改善。有没有关于如何通过减少for循环次数来提高代码速度的建议?谢谢!

3 个答案:

答案 0 :(得分:2)

如果你可以将dict值转换为numpy数组,那么有一些加速:

数据:

intervals_numpy = {'Sca1': np.array([[1428, 1876, 0.0126525], [1876, 1883, 0.0126525], [1883, 1939, 0.0126525], [1939, 1956, 0.0126525], [1956, 2032, 0.0126525], [2154, 3067, 0.0126525], [3067, 3438, 0.0126525], [3438, 3575, 0.0126525], [4301, 4610, 0.0126525], [4610, 4694, 0.0126525], [4694, 5163, 0.0126525], [5163, 5164, 0.0126525], [5164, 5530, 0.013], [5530, 5858, 0.0127005]])}

intervals_list = {'Sca1': [[1428, 1876, 0.0126525], [1876, 1883, 0.0126525], [1883, 1939, 0.0126525], [1939, 1956, 0.0126525], [1956, 2032, 0.0126525], [2154, 3067, 0.0126525], [3067, 3438, 0.0126525], [3438, 3575, 0.0126525], [4301, 4610, 0.0126525], [4610, 4694, 0.0126525], [4694, 5163, 0.0126525], [5163, 5164, 0.0126525], [5164, 5530, 0.013], [5530, 5858, 0.0127005]]}

snplist = [1786, 2463, 2907, 3068, 3086, 3398, 5468, 5531, 5564, 5580]

功能:

def foo(intervals, snplist):
    output=[]
    for n in snplist:
        for key, value in intervals.items():
            for idx in np.where( np.logical_and(value[:,0] < n, n < value[:,1]) )[0]:
                output.append([idx, n, value[idx][2]])
    return output

def bar(intervals, snplist):
    output=[]
    for element in snplist:
        for key, value in intervals.items():
            for left, right, rho in value:
                if left <= element <= right:
                    output.append([value.index([left, right, rho]), element, rho])
    return output

在此设置中,与bar相比,foo的速度大约是我的三倍:

%timeit bar(intervals_list, snplist)
The slowest run took 6.22 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 13.5 µs per loop

%timeit foo(intervals_numpy, snplist)
The slowest run took 5.99 times longer than the fastest. This could mean that an intermediate result is being cached 
10000 loops, best of 3: 39.8 µs per loop

但numpy为大型阵列带来了回报!在此设置中,速度提高了约500倍:

intervals_numpy['Sca1'] = np.repeat(intervals_numpy['Sca1'], 1000, axis=0)
intervals_list['Sca1'] = intervals_numpy['Sca1'].tolist()

%timeit bar(intervals_list, snplist)
1 loops, best of 3: 2.05 s per loop

%timeit foo(intervals_numpy, snplist)
100 loops, best of 3: 4.04 ms per loop

这种巨大的速度差异主要是因为您的索引查找,请参阅Martin Evans的答案。但是我的numpy版本还是要快一点。

答案 1 :(得分:1)

三件事:

  1. 通过使用<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" /> 删除索引查找,可以获得一些小的改进。

  2. 使用itertools.product也应该有助于迭代。

  3. 您目前没有使用enumerate,因此可以使用key

  4. 这些将为您提供以下内容:

    .values()

答案 2 :(得分:1)

您可以尝试更改迭代的顺序。

如果snplist是最小的,并且说你的例子中有10个元素,intervals例如10个 6 个键,每个10个元素,为简单起见,我们说迭代任何事情需要花费时间1个单位时间(ut)为每1000个元素然后在这个风景中我们有你所有的时间在intervals迭代1000ut。

然后用你当前的方式

for element in snplist:
    for key, value in intervals.items():
        ...

需要10000ut来完成这项工作。

将其更改为

for key, value in intervals.items():
    for element in snplist:
        ...

所以只需要1000ut,因为你根本不关心你的代码中的密钥应该不是问题。

一般情况下,首先尝试迭代大的,然后重复最小的。