将Numpy数组转换为稀疏字典的最快方法?

时间:2009-05-18 12:52:22

标签: python performance numpy

我有兴趣尽快将numpy数组转换为稀疏字典。让我详细说明一下:

给定数组:

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

有更快的方法吗?

编辑: 步骤

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

看起来特别笨拙 - 在选择一些元素之前生成整个数组,特别是当我们知道它可能只有大约1/10的元素被选中时。我认为这可能仍有待改进。

6 个答案:

答案 0 :(得分:2)

我想知道主要的时间成本是否随着它的增长而重新调整字典。

如果dict有一个方法(或实例化选项)来指定一个起始大小,那就太好了;因此,如果我们知道它会变得很大,python可以节省时间,只需要预先做一个大的内存分配,而不是我假设随着我们的增长而增加额外的分配。

答案 1 :(得分:2)

>>> 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 :(得分:0)

使用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

答案 3 :(得分:0)

以下似乎是一项重大改进:

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_matrixpandas.DataFrame.to_dict,但在我的测试中,它们比原来的要慢。

答案 4 :(得分:0)

您可以将np.uniquereturn_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)

答案 5 :(得分:-1)

试过这个?

来自numpy import的地方

i = where(vector&gt; 0)[0]