Есть ли более простой и быстрый способ получить индекс индексов, в котором содержатся индексы тех же элементов в списке или массиве numpy

Описание:

У меня есть большой массив с простыми целыми числами (положительными и небольшими), такими как 1, 2, ... и т. д. Например: [1, 1, 2, 2, 1, 2]. Я хочу получить dict, в котором использовать одно значение из списка в качестве ключа dict и использовать список индексов этого значения в качестве значения dict.

Вопрос:

Есть ли более простой и быстрый способ получить ожидаемые результаты в Python? (массив может быть списком или массивом numpy)

Код:

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

Ожидаемые результаты:

{1:[0, 1, 4], 2:[2, 3, 5]}
1
0
113
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Создать диктант довольно просто:

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).

Спасибо за ответ. Есть ли встроенный метод в python или numpy для выполнения аналогичной работы?

zhenyu wang 26.10.2018 05:28

Вы можете избежать итераций здесь, используя векторизованные методы, в частности 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)
Ответ принят как подходящий

Мы можем использовать тот факт, что элементы являются «простыми» (т.е. неотрицательными и не слишком большими?) Целыми числами.

Уловка состоит в том, чтобы построить разреженную матрицу с одним элементом в строке, а затем преобразовать ее в представление по столбцам. Обычно это быстрее, чем argsort, потому что это преобразование O (M + N + nnz), если разреженная матрица - это MxN с 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

Отлично, думаю надо поменять "проще" на "быстрее". :) Большое тебе спасибо

zhenyu wang 28.10.2018 03:17

Другие вопросы по теме