有没有更简单快捷的方法来获取索引dict,其中包含列表或numpy数组中相同元素的索引

时间:2018-10-26 03:16:47

标签: python arrays numpy indexing

说明:

我有一个包含简单整数(正数和不大数)的大型数组,例如1,2,...等。例如:[1、2、1、2、1、2]。我想要一个字典,其中使用列表中的单个值作为字典的键,并使用此值的索引列表作为字典的值。

问题:

是否有更简单,更快速的方法来在python中获得预期的结果? (数组可以是列表或numpy数组)

代码:

<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.41.0/codemirror.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.41.0/codemirror.min.css" rel="stylesheet"></link>
<textarea id="editor"><p>A paragraph</p></textarea>

预期结果:

a = [1, 1, 2, 2, 1, 2]
results = indexes_of_same_elements(a)
print(results)

3 个答案:

答案 0 :(得分:2)

您可以在这里避免使用矢量化方法进行迭代,尤其是np.unique + np.argsort

idx = np.argsort(a)
el, c = np.unique(a, return_counts=True)

out = dict(zip(el, np.split(idx, c.cumsum()[:-1])))

{1: array([0, 1, 4], dtype=int64), 2: array([2, 3, 5], dtype=int64)} 

性能

a = np.random.randint(1, 100, 10000)

In [183]: %%timeit
     ...: idx = np.argsort(a)
     ...: el, c = np.unique(a, return_counts=True)
     ...: dict(zip(el, np.split(idx, c.cumsum()[:-1])))
     ...:
897 µs ± 41.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [184]: %%timeit
     ...: results = {}
     ...: for i, k in enumerate(a):
     ...:     results.setdefault(k, []).append(i)
     ...:
2.61 ms ± 18.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

答案 1 :(得分:1)

构造字典非常简单:

In []:
results = {}
for i, k in enumerate(a):
    results.setdefault(k, []).append(i)   # str(k) if you really need the key to be a str
print(results)

Out[]:
{1: [0, 1, 4], 2: [2, 3, 5]}

您还可以先使用results = collections.defaultdict(list),然后再使用results[k].append(i)代替results.setdefault(k, []).append(i)

答案 2 :(得分:1)

我们可以利用以下事实:元素是“简单的”(即非负且不是太大?)整数。

诀窍是构造一个稀疏矩阵,每行仅包含一个元素,然后将其转换为按列表示。这通常比argsort快,因为如果稀疏矩阵是nx非零的MxN,则此变换为O(M + N + nnz)。

from scipy import sparse

def use_sprsm():
    x = sparse.csr_matrix((a, a, np.arange(a.size+1))).tocsc()
    idx, = np.where(x.indptr[:-1] != x.indptr[1:])
    return {i: a for i, a in zip(idx, np.split(x.indices, x.indptr[idx[1:]]))}

# for comparison

def use_asort():
    idx = np.argsort(a)
    el, c = np.unique(a, return_counts=True)
    return dict(zip(el, np.split(idx, c.cumsum()[:-1])))

样品运行:

>>> a = np.random.randint(0, 100, (10_000,))
>>> 
# sanity check, note that `use_sprsm` returns sorted indices
>>> for k, v in use_asort().items():
...     assert np.array_equal(np.sort(v), use_sprsm()[k])
... 
>>> timeit(use_asort, number=1000)
0.8930604780325666
>>> timeit(use_sprsm, number=1000)
0.38419671391602606