我有一个JSON字符串,其中包含一个字典映射到float
值的索引。这是矢量的代表。例如,
{
'distro': {0: 2.42, 3: 2.56},
'constant': 4.55
'size': 10000
}
表示大小为10000
的向量,索引为2.42
0
,索引2.56
为2
。此向量中的所有其他值均为4.55
。
表示此数据结构的高效方式是什么? scipy.sparse
会帮帮我吗?我的主要应用是快速创建密集表示,但我不想事先将它们存储在内存中(因为有很多这样的向量)。
答案 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())
(我假设keys
,values
和items
以相同的键顺序返回值。
对于这个小案例,我怀疑普通的迭代方法更快。但是后者更大的东西可能更好。我无法预测交叉发生的位置。
scipy.sparse
生成2d矩阵。它没有实现任何类型的const
填充。 (Pandas稀疏确实有这样的填充)。我们当然可以从sparse
和dd['size']
构建一个dd['distro']
矩阵。但我不知道它是否会提供任何速度优势。
如果Tensorflow是你真正的目标,那么你可能需要更多地了解它的构造方法。也许您根本不需要通过numpy
或sparse
。
没有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)