存储此向量的最有效方法是什么?

时间:2017-03-23 17:56:01

标签: python numpy scipy

我有一个JSON字符串,其中包含一个字典映射到float值的索引。这是矢量的代表。例如,

{
    'distro': {0: 2.42, 3: 2.56},
    'constant': 4.55
    'size': 10000
}

表示大小为10000的向量,索引为2.42 0,索引2.562。此向量中的所有其他值均为4.55

表示此数据结构的高效方式是什么? scipy.sparse会帮帮我吗?我的主要应用是快速创建密集表示,但我不想事先将它们存储在内存中(因为有很多这样的向量)。

2 个答案:

答案 0 :(得分:1)

我想你的迭代方式是这样的:

In [204]: dd = {
     ...:     'distro': {0: 2.42, 3: 2.56},
     ...:     'constant': 4.55,
     ...:     'size': 10,
     ...: }
In [205]: dd
Out[205]: {'constant': 4.55, 'distro': {0: 2.42, 3: 2.56}, 'size': 10}

In [207]: x = np.zeros(dd['size'])
In [208]: x[:] = dd['constant']
In [210]: for i,v in dd['distro'].items():
     ...:     x[i] = v
In [211]: x
Out[211]: array([ 2.42,  4.55,  4.55,  2.56,  4.55,  4.55,  4.55,  4.55,  4.55,  4.55])

x[:]的替代方法是x.fill(dd['constant']),但不要认为速度有太大差异。

这是一种在没有显式迭代的情况下从字典中设置值的方法:

In [221]: ddvals = np.array(list(dd['distro'].items()),dtype='i,f')
In [222]: ddvals
Out[222]: 
array([(0,  2.42000008), (3,  2.55999994)], 
      dtype=[('f0', '<i4'), ('f1', '<f4')])
In [223]: x[ddvals['f0']]=ddvals['f1']
In [224]: x
Out[224]: 
array([ 2.42000008,  4.55      ,  4.55      ,  2.55999994,  4.55      ,
        4.55      ,  4.55      ,  4.55      ,  4.55      ,  4.55      ])

或没有结构化数组:

In [225]: vals = np.array(list(dd['distro'].items()))
In [226]: vals
Out[226]: 
array([[ 0.  ,  2.42],
       [ 3.  ,  2.56]])
In [227]: x[vals[:,0]] = vals[:,1]
...
IndexError: arrays used as indices must be of integer (or boolean) type
In [228]: x[vals[:,0].astype(int)] = vals[:,1]
In [229]: x
Out[229]: array([ 2.42,  4.55,  4.55,  2.56,  4.55,  4.55,  4.55,  4.55,  4.55,  4.55])

字典items()(或PY3中的list(items()))给出了元组列表。较新的numpy版本不喜欢使用浮点数作为索引,因此我们必须添加几个步骤来保留整数键值。

这可能是最简单的:

x[list(dd['distro'].keys())] = list(dd['distro'].values())

(我假设keysvaluesitems以相同的键顺序返回值。

对于这个小案例,我怀疑普通的迭代方法更快。但是后者更大的东西可能更好。我无法预测交叉发生的位置。

scipy.sparse生成2d矩阵。它没有实现任何类型的const填充。 (Pandas稀疏确实有这样的填充)。我们当然可以从sparsedd['size']构建一个dd['distro']矩阵。但我不知道它是否会提供任何速度优势。

如果Tensorflow是你真正的目标,那么你可能需要更多地了解它的构造方法。也许您根本不需要通过numpysparse

没有x的{​​{1}}可以表示为const稀疏矩阵,其中包含:

scipy

其关键属性是:

In [247]: Xo = sparse.coo_matrix([x])
In [248]: Xo
Out[248]: 
<1x10 sparse matrix of type '<class 'numpy.float64'>'
    with 2 stored elements in COOrdinate format>

In [249]: Xo.data Out[249]: array([ 2.42, 2.56]) In [250]: Xo.row Out[250]: array([0, 0], dtype=int32) In [251]: Xo.col Out[251]: array([0, 3], dtype=int32) In [252]: Xo.shape Out[252]: (1, 10) Xr=Xo.tocsr()格式类似,但csr属性替换为row数组,每行有一个值(+1),所以它不会随着非零条款的数量而增长。它用于大多数稀疏数学。

还有indptr格式,实际上是字典子类:

dok

如果输入有效In [258]: dict(Xo.todok()) Out[258]: {(0, 0): 2.4199999999999999, (0, 3): 2.5600000000000001} ,则需要将索引键转换为整数。

json

来自SO搜索的我的印象是,In [281]: jstr Out[281]: '{"distro": {"0": 2.42, "3": 2.56}, "constant": 4.55, "size": 10}' In [282]: jdd = json.loads(jstr) In [283]: jdd Out[283]: {'constant': 4.55, 'distro': {'0': 2.42, '3': 2.56}, 'size': 10} In [284]: list(jdd['distro'].keys()) Out[284]: ['0', '3'] In [285]: np.array(list(jdd['distro'].keys()),int) Out[285]: array([0, 3]) In [286]: np.array(list(jdd['distro'].values())) Out[286]: array([ 2.42, 2.56]) json.load一样快,如果不快。它必须解析一个更简单的语法。

python eval vs ast.literal_eval vs JSON decode

如果您可以处理eval字符串,并将它们存储在某种中间数据结构中,则有几种可能性。这些载体的“稀疏”程度如何?如果字典具有几乎所有1000个“大小”条目的值,则最好构建完整的numpy数组并保存该数组(例如,使用json对)。

如果它是稀疏的(比如10%的值是非const),保存2个索引和值数组可能更有意义(出285和284)。要么将它们分开,要么将它们加入我之前生成的结构化数组中。

答案 1 :(得分:1)

根据我们的讨论,我提出了一个将json转换为简单(快速)解析二进制格式的示例。看到评论的描述。我使用BytesIO进行测试,但您可能只是使用以'rb'模式打开的文件。

import numpy as np
import struct
import io


test_json = '''{
    'distro': {0: 2.42, 3: 2.56},
    'constant': 4.55,
    'size': 10000
}'''

#this will be slow but only have to take place once..
def json_to_bin(test_json):
    j = eval(test_json) #json provided was not well formed json, but was well formed python dict..
    const = j['constant']
    size = j['size']
    n = len(j['distro'])
    keys = j['distro'].keys()
    vals = [j['distro'][key] for key in keys]

    buf = io.BytesIO() #dummy file like object
    #struct.pack args:
        # format_string, *values_described_in_format_string
    buf.write(struct.pack("dii{0}i{0}d".format(n), const, size, n, *keys, *vals))
    return buf

def bin_to_arr(buf):
    buf.seek(0)
    #unpack our first three values 
    #most important is n so we can unpack the right number of keys and values
    const, size, n = struct.unpack("dii", buf.read(16)) #struct.calcsize('dii') = 16
    #unpack keys
    fmt = "{}i".format(n)
    nbytes = struct.calcsize(fmt)
    keys = np.array(struct.unpack(fmt, buf.read(nbytes))) #returns array (for indexing)
    #unpack vals
    fmt = "{}d".format(n)
    nbytes = struct.calcsize(fmt)
    vals = struct.unpack(fmt, buf.read(nbytes)) #returns tuple
    #create ndarray
    arr = np.empty(size)
    arr.fill(const)
    arr[keys] = vals
    return arr

print(test_json)
print('=======binary========')
buf = json_to_bin(test_json)
buf.seek(0)
print(buf.read())
arr = bin_to_arr(buf)