Python:更快的索引操作

前端之家收集整理的这篇文章主要介绍了Python:更快的索引操作前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有以下片段,它使用规范索引在序列式数据中提取所有唯一值(可散列)的索引,并将它们作为列表存储在字典中:
from collections import defaultdict
idx_lists = defaultdict(list)
for idx,ele in enumerate(data):
    idx_lists[ele].append(idx)

这对我来说是一个很常见的用例.而且我的代码执行时间的90%花在了这几行上.该部分在执行期间传递超过10000次,并且每次运行时len(数据)大约为50000到100000.独特元素的数量大致为50到150.

有没有更快的方法,也许是矢量化/ c扩展(例如numpy或pandas方法),实现了同样的目的?

非常感谢.

解决方法

我发现这个问题非常有趣,虽然我无法对其他提出的方法进行大的改进,但我确实发现了一种比其他方法略快的纯粹numpy方法.
import numpy as np
import pandas as pd
from collections import defaultdict

data = np.random.randint(0,10**2,size=10**5)
series = pd.Series(data)

def get_values_and_indicies(input_data):
    input_data = np.asarray(input_data)
    sorted_indices = input_data.argsort() # Get the sorted indices
    # Get the sorted data so we can see where the values change
    sorted_data = input_data[sorted_indices]
    # Find the locations where the values change and include the first and last values
    run_endpoints = np.concatenate(([0],np.where(sorted_data[1:] != sorted_data[:-1])[0] + 1,[len(input_data)]))
    # Get the unique values themselves
    unique_vals = sorted_data[run_endpoints[:-1]]
    # Return the unique values along with the indices associated with that value
    return {unique_vals[i]: sorted_indices[run_endpoints[i]:run_endpoints[i + 1]].tolist() for i in range(num_values)}


def by_dd(input_data):
    idx_lists = defaultdict(list)
    for idx,ele in enumerate(input_data):
        idx_lists[ele].append(idx)
    return idx_lists

def by_pand1(input_data):
    idx_lists = defaultdict(list)
    return {k: v.tolist() for k,v in series.groupby(input_data).indices.items()}

def by_pand2(input_data):
    return series.groupby(input_data).indices

def data_to_idxlists(input_data):
    u,ixs = np.unique(input_data,return_inverse=True)
    return {u: np.nonzero(ixs==i) for i,u in enumerate(u)}

def data_to_idxlists_unique(input_data):
    sorting_ixs = np.argsort(input_data)
    uniques,unique_indices = np.unique(input_data[sorting_ixs],return_index = True)
    return {u: sorting_ixs[start:stop] for u,start,stop in zip(uniques,unique_indices,list(unique_indices[1:])+[None])}

由此产生的时间(从最快到最慢):

>>> %timeit get_values_and_indicies(data)
100 loops,best of 3: 4.25 ms per loop
>>> %timeit by_pand2(series)
100 loops,best of 3: 5.22 ms per loop
>>> %timeit data_to_idxlists_unique(data)
100 loops,best of 3: 6.23 ms per loop
>>> %timeit by_pand1(series)
100 loops,best of 3: 10.2 ms per loop
>>> %timeit data_to_idxlists(data)
100 loops,best of 3: 15.5 ms per loop
>>> %timeit by_dd(data)
10 loops,best of 3: 21.4 ms per loop

应该注意的是,与by_pand2不同,它会产生一个列表的字典,如示例中给出的.如果你更愿意返回一个defaultdict,你可以简单地改变最后一次返回defaultdict(list,((unique_vals [i],sorted_indices [run_endpoints [i]:run_endpoints [i 1]].tolist())for i in range (num_values)))这使我的测试中的总体时间增加到4.4毫秒.

最后,我应该注意到这些时间对数据敏感.当我使用10个不同的值时,我得到了:

> get_values_and_indicies:每个循环4.34毫秒
> data_to_idxlists_unique:每个循环4.42 ms
> by_pand2:每循环4.83 ms
> data_to_idxlists:每个循环6.09毫秒
> by_pand1:每个循环9.39 ms
> by_dd:每个循环22.4毫秒

如果我使用了10,000个不同的值,我得到了:

> get_values_and_indicies:每个循环7.00毫秒> data_to_idxlists_unique:每个循环14.8 ms> by_dd:每个循环29.8 ms> by_pand2:每个循环47.7毫秒> by_pand1:每个循环67.3毫秒> data_to_idxlists:每个循环869 ms

猜你在找的Python相关文章