Самый быстрый способ преобразовать массив Numpy в разреженный словарь? - PullRequest
5 голосов
/ 18 мая 2009

Я заинтересован в том, чтобы как можно быстрее преобразовать пустой массив в разреженный словарь. Позвольте мне уточнить:

Учитывая массив:

numpy.array([12,0,0,0,3,0,0,1])

Я хочу изготовить словарь:

{0:12, 4:3, 7:1}

Как видите, мы просто конвертируем тип последовательности в явное отображение из индексов, которые не равны нулю, в их значения.

Чтобы сделать это немного более интересным, я предлагаю следующий тестовый набор, чтобы опробовать альтернативы:

from timeit import Timer

if __name__ == "__main__":
  s = "import numpy; from itertools import izip; from numpy import nonzero, flatnonzero; vector =         numpy.random.poisson(0.1, size=10000);"

  ms = [ "f = flatnonzero(vector); dict( zip( f, vector[f] ) )"
             , "f = flatnonzero(vector); dict( izip( f, vector[f] ) )"
             , "f = nonzero(vector); dict( izip( f[0], vector[f] ) )"
             , "n = vector > 0; i = numpy.arange(len(vector))[n]; v = vector[n]; dict(izip(i,v))"
             , "i = flatnonzero(vector); v = vector[vector > 0]; dict(izip(i,v))"
             , "dict( zip( flatnonzero(vector), vector[flatnonzero(vector)] ) )"
             , "dict( zip( flatnonzero(vector), vector[nonzero(vector)] ) )"
             , "dict( (i, x) for i,x in enumerate(vector) if x > 0);"
             ]
  for m in ms:
    print "  %.2fs" % Timer(m, s).timeit(1000), m

Я использую распределение Пуассона для имитации того типа массивов, которые меня интересуют.

Вот мои результаты:

   0.78s f = flatnonzero(vector); dict( zip( f, vector[f] ) )
   0.73s f = flatnonzero(vector); dict( izip( f, vector[f] ) )
   0.71s f = nonzero(vector); dict( izip( f[0], vector[f] ) )
   0.67s n = vector > 0; i = numpy.arange(len(vector))[n]; v = vector[n]; dict(izip(i,v))
   0.81s i = flatnonzero(vector); v = vector[vector > 0]; dict(izip(i,v))
   1.01s dict( zip( flatnonzero(vector), vector[flatnonzero(vector)] ) )
   1.03s dict( zip( flatnonzero(vector), vector[nonzero(vector)] ) )
   4.90s dict( (i, x) for i,x in enumerate(vector) if x > 0);

Как видите, самое быстрое решение, которое я нашел, это

n = vector > 0;
i = numpy.arange(len(vector))[n]
v = vector[n]
dict(izip(i,v))

Есть ли более быстрый способ?

Edit: Шаг

i = numpy.arange(len(vector))[n]

Кажется особенно неуклюжим - генерирование всего массива перед выбором только некоторых элементов, особенно когда мы знаем, что может быть выбрано только около 1/10 элементов Я думаю, что это все еще может быть улучшено.

Ответы [ 6 ]

2 голосов
/ 31 октября 2014
>>> a=np.array([12,0,0,0,3,0,0,1])
>>> {i:a[i] for i in np.nonzero(a)[0]}
{0: 12, 4: 3, 7: 1}
2 голосов
/ 18 мая 2009

Интересно, из-за того, что основные затраты времени меняют размер диктата по мере его роста?

Было бы неплохо, если бы в dict был метод (или опция создания экземпляра) для определения начального размера; поэтому, если бы мы знали, что он будет большим, python мог бы сэкономить время и просто сделать одно большое выделение памяти заранее, а не то, что я считаю дополнительными выделениями по мере роста.

0 голосов
/ 20 мая 2017

Вы можете использовать np.unique с return_index=True:

>>> import numpy as np

>>> arr = np.array([12,0,0,0,3,0,0,1])

>>> val, idx = np.unique(arr, return_index=True)
>>> mask = val != 0                                # exclude zero
>>> dict(zip(idx[mask], val[mask]))                # create the dictionary
{0: 12, 4: 3, 7: 1}

Обычно итерация выполняется быстрее list с, чем numpy.array с, поэтому вы можете быстрее выполнять преобразование их в списки с tolist:

>>> dict(zip(idx[mask].tolist(), val[mask].tolist()))

Сроки

Для коротких массивов этот подход может быть медленнее, но, согласно моим временам, он быстрее, чем другие подходы для больших массивов:

import numpy as np
from scipy.sparse import csr_matrix

arr = np.random.randint(0, 10, size=10000)  # 10k items
arr[arr < 7] = 0                            # make it sparse

# ----------

%timeit {i:arr[i] for i in np.nonzero(arr)[0]}
# 3.7 ms ± 51 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# ----------

%%timeit

val, idx = np.unique(arr, return_index=True)
mask = val != 0
dict(zip(idx[mask].tolist(), val[mask].tolist()))

# 844 µs ± 42.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# ----------

%%timeit

m=csr_matrix(a)

d={}
for i in m.nonzero()[1]:
    d[i]=m[0,i]

# 1.52 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
0 голосов
/ 31 октября 2014

Следующее представляется значительным улучшением:

i = np.flatnonzero(vector)
dict.fromkeys(i.tolist(), vector[i].tolist())

Сроки:

import numpy as np
from itertools import izip

vector = np.random.poisson(0.1, size=10000)

%timeit f = np.flatnonzero(vector); dict( izip( f, vector[f] ) )
# 1000 loops, best of 3: 951 µs per loop

%timeit f = np.flatnonzero(vector); dict.fromkeys(f.tolist(), vector[f].tolist())
# 1000 loops, best of 3: 419 µs per loop

Я также пробовал scipy.sparse.dok_matrix и pandas.DataFrame.to_dict, но в моем тестировании они были медленнее, чем оригинал.

0 голосов
/ 01 июня 2011

использовать разреженную матрицу в scipy в качестве моста:

from scipy.sparse import *
import numpy
a=numpy.array([12,0,0,0,3,0,0,1])
m=csr_matrix(a)

d={}
for i in m.nonzero()[1]:
  d[i]=m[0,i]
print d
0 голосов
/ 26 июня 2009

пробовал это?

из импортного импорта, где

i = где (вектор> 0) [0]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...