Интересной альтернативой вашему коду является преобразование массива в формат COOrdinate , а затем чтение его атрибутов row и col :
def nonzero_indices_by_coo(input):
cx = input.T.tocoo()
res = [ [] for i in range(cx.shape[0]) ]
for i, j in zip(cx.row, cx.col):
res[i].append(j)
return res
Он возвращает список простых списков pythoni c вместо массивов Numpy, но это не должно быть важной разницей.
Я заметил, что ваш код использует внутреннее транспонирование исходного массива (оператор T ), поэтому я сделал то же самое в своем коде.
Чтобы сравнить скорость выполнения, я создал следующий разреженный массив ( 2000 по 300 ):
r = 2000; c = 300
x = scipy.sparse.lil_matrix( (r,c) )
for _ in range(r):
x[np.random.randint(0,r-1), np.random.randint(0,c-1)] = np.random.randint(1,100)
и мой код работал примерно в 12 раз быстрее, чем ваш.
Еще более быстрое решение (в другом формате)
Или, может быть, будет лучше сгенерировать массив 2-D (Numpy) с 2 строками:
- первая строка - индексы строк из последовательных ненулевых элементов,
- вторая строка - индексы столбцов.
Для получения такого результата вы можете использовать e следующий код:
def nonzero_indices_2d(input):
cx = input.T.tocoo()
return np.array([cx.row, cx.col])
, который работает в 4 раза быстрее, чем мое первое решение.
Конечно, тогда другие части вашего кода должны быть переработаны, чтобы использовать индексы, указанные в другом формат.
Разреженные массивы также имеют свой собственный ненулевой метод:
arr.nonzero()
создание 2-строчного Numpy массив индексов. Эта функция работает еще на несколько процентов быстрее.
Итак, если предположить, что формат результата 2-D приемлем (вместо списка списков), возможно, вам не нужен собственный функция для получения этих индексов.
Еще одна деталь, которую следует учитывать: следует ли (во всех версиях) использовать транспонирование. Ваш выбор, но без транспонирования каждая версия кода будет работать немного быстрее.